我需要根据另一列的值创建一个自动增量列,并且我创建了以下触发器:
CREATE TRIGGER [dbo].[setPosition]
ON [dbo].[myTable]
AFTER INSERT
AS
BEGIN
DECLARE @maxPosition SMALLINT
DECLARE @templateName VARCHAR(100)
DECLARE @id INT
SELECT @id=id FROM inserted
SELECT @templateName=templateName FROM inserted
SELECT @maxPosition=ISNULL(MAX(position),0) FROM [myTable] WHERE [templateName]=@templateName
UPDATE [myTable]
SET position = @maxPosition+1 WHERE id=@id
END
它有效,但我有一个担忧。我有可能有双号吗?例如,如果它在具有许多用户的环境中运行。我有一个类似的情况here,其中为了确保先前的关注在首先创建索引时进行循环检查以查找错误,并在没有错误时进行更新。但我相信这样的检查是徒劳的,因为据我所知,触发器在隔离事务模式下运行。
已更新
插入后的数据就像这样(id是自增列)
对于您正在尝试做的事情,有更好的解决方案。您可以先在应用程序/客户端分配一个 ID,或者在执行插入操作的 SQL 代码中分配一个 ID。或者您可以为每个模板使用
SEQUENCE
。或者一个随机数。或者忘记整个事情,只在查询时对行进行编号。
但是,如果您真的决定使用触发器,则需要考虑多行或零行,并且需要考虑同一行的多个行
templateName
。您还需要正确锁定 dbo.myTable
。
所以它应该看起来像这样。
CREATE TRIGGER dbo.setPosition
ON dbo.myTable
AFTER INSERT
AS
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM inserted) -- early bailout
RETURN;
UPDATE t
SET position = others.maxPosition + i.rn
FROM (
SELECT *,
-- need to number the inserts in case of multiple per templateName
rn = ROW_NUMBER() OVER (PARTITION BY i.templateName ORDER BY i.id)
FROM inserted i
) i
JOIN dbo.myTable t ON t.id = i.id
CROSS APPLY (
SELECT
maxPosition = ISNULL(MAX(position),0)
FROM dbo.myTable others WITH (SERIALIZABLE, UPDLOCK)
WHERE others.templateName = i.templateName
) others;
确保您的表已在
templateName
上建立索引,否则您最终会遇到严重的阻塞和死锁问题。