我正在研究一个需要在另一个表上重新插入数据的触发器。
目标表有一个主键INT NOT NULL WITHOUT Identity,所以我有两个选择:
我总是创建一个带有标识的表变量,并从之前计算的值插入移位。
CREATE TRIGGER trg
ON [dbo].[table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @t TABLE (sec INT IDENTITY(1, 1), id INT)
DECLARE @ini INT
SELECT @ini = ISNULL(MAX(id), 0) FROM tableDest
-- SELECT @ini = value FROM sequencesTable WHERE seqId = 987
INSERT INTO @t (id) SELECT id FROM inserted
INSERT INTO tableDest
(id, field1, field2)
SELECT @ini + t.sec, field1, field2
FROM @t t
JOIN inserted ON t.id = inserted.id
-- SELECT @ini = @ini + MAX(t.sec) FROM @t
-- UPDATE sequencesTable SET value = @ini WHERE seqId = 987
END
有没有更好的方法呢?
先谢谢。
答案 0 :(得分:2)
假设SQL 2005+,您可以使用ROW_NUMBER():
CREATE TRIGGER trg
ON [dbo].[table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO tableDest
(id, field1, field2)
SELECT
Seed.Value + ROW_NUMBER() OVER(ORDER BY Id),
field1,
field2
FROM Inserted
CROSS JOIN (
SELECT MAX(id) as Value
FROM tableDest
) as Seed
END
执行CROSS JOIN
而不是直接获取种子值可以使您免于MAX(Id)
在获取值和插入值之间发生变化的并发问题。否则,您需要SERIALIZABLE
事务来防止在读取新行后将其插入tableDest。
答案 1 :(得分:1)
只是为了澄清一下,如果我运行这个语句,它会不会在表Foo中给我一个唯一的Bar,即使同时执行多个同时语句并行执行?
INSERT INTO Foo (Bar)
SELECT
CASE WHEN Bar = -1
THEN (SELECT Seed.Value + ROW_NUMBER() OVER(ORDER BY AutoId)
FROM inserted CROSS JOIN (SELECT MAX(Bar) as Value FROM Foo) as Seed)
ELSE Bar
END
在这种情况下,是什么让语句并发安全?是因为为Bar生成新值的select嵌套在insert语句中?或者是交叉连接阻止对表Foo的同时读取访问? case语句怎么样,它在并发性方面对结果有影响吗?
谢谢!
答案 2 :(得分:0)
tableDest 真的是否需要人工主键?
此外,并发:在计算@ini的值和实际执行插入之间是否有可能在tableDest上发生其他插入?
答案 3 :(得分:0)
我想我会问一个明显的问题,为什么不在目标表上设置一个标识值? (注意,如果你这样做,请确保你没有使用@@ identity来获取源表中的标识值,因为这不是你将获得的标识值 - 你将从目标表中获取标识。 )
或者您可以使用源表中的标识作为另一个表的标识。这让我觉得这是设计的初衷。否则,可能很难将这些记录与原始数据相匹配。