插入具有唯一 ID 的记录时无法在触发器上执行多行插入
Cannot perform multi-row insert on trigger when inserting record with unique id
这是我的触发器:
ALTER TRIGGER DONORINFO_INSERT
ON [dbo].[DONORINFO] INSTEAD OF INSERT
AS
DECLARE @sequence AS VARCHAR(50) = ''
DECLARE @tranLen VARCHAR(10)
SET @sequence = (SELECT TOP 1 SUBSTRING([DONORID], 3, 8)
FROM [dbo].[DONORINFO]
ORDER BY [DONORID] DESC)
IF (@sequence IS NULL OR @sequence = '')
BEGIN
SELECT @sequence = REPLICATE('0', 7 ) + '1'
END
ELSE
BEGIN
SELECT @tranLen = LEN(@sequence)
SELECT @sequence = @sequence + 1
SELECT @tranLen = ABS(@tranLen - LEN(CAST(@sequence AS INT)))
SELECT @sequence = REPLICATE('0', @tranLen) + @sequence
END
DECLARE @DONORID AS [nvarchar](50) = 'DN' + CONVERT(VARCHAR, @sequence)
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT @DONORID, inserted.DONORNAME
FROM inserted
在脚本的第一行,我正在阅读 DONORINFO
table,我在其中检查了唯一 ID 是否存在。之后,我将把记录插入到那个 table 中。我第一次测试时,insert into select 脚本有效,但第二次时,它失败并发送并违反了主键错误。
但是如果我逐行测试插入,它会起作用。
这是行之有效的逐行插入脚本。
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
VALUES ('DN00000001', 'test')
如果我运行它两次,记录将是这样的:
DONORID DONORNAME
---------------------
DN00000001 test
DN00000002 test
这是对 select 脚本的插入,但不起作用:
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT
'',
[NameOfDonor]
FROM
[dbo].[_TEMPENDOWMENTFUND] AS ENDF
WHERE
[ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME]
FROM [dbo].[DONORINFO])
_TEMPDOWMENTFUND
是我创建的 table,它将存储从 Excel 工作表迁移的数据,触发器的目的是生成一个唯一的 DONORID
对于 DONORINFO
table 上插入的每条记录。
现在我的问题是,我想执行 insert into select 语句,这是一个多行插入,但我很难弄清楚我创建的触发器出了什么问题.
如有任何帮助,我们将不胜感激。谢谢。
将@sequence
完全int
(可能重命名为last_id
),在最后添加字符。
要对行进行编号,请使用 ROW_NUMBER()
in final select from INSERTED
:
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT
'DN' + REPLICATE('0', ABS(@len_you_need - LEN(t.generated_id))) + CAST (t.generated_id as varchar(100)),
t.DONORNAME
FROM
(
SELECT
i.DONORNAME,
@sequence+ROW_NUMBER()OVER(ORDER BY i.DONORNAME) as generated_id
FROM inserted i
) t
@len_you_need
- 是您需要的 DONORID
的长度。我猜这可能是一个8个字符的常量。在您的来源中,您在这里计算:
SELECT @tranLen = LEN(@sequence)
t.rn
是在上面给出的子查询中生成的 "sequence" 值,它有 t
别名。为清楚起见,将其重命名为 generated_id
。
这个区块:
BEGIN
SELECT @tranLen = LEN(@sequence)
SELECT @sequence = @sequence + 1
SELECT @tranLen = ABS(@tranLen - LEN(CAST(@sequence AS INT)))
SELECT @sequence = REPLICATE('0', @tranLen) + @sequence
END
不再需要了。
这是@ivan-starostin 帮我解答的完整解决方案。
ALTER TRIGGER DONORINFO_INSERT ON [dbo].[DONORINFO]
INSTEAD OF INSERT, UPDATE
AS
DECLARE @sequence AS VARCHAR(50) = ''
DECLARE @tranLen VARCHAR(10)
SET @sequence = (SELECT TOP 1 SUBSTRING([DONORID], 3, 8) FROM [dbo].[DONORINFO] ORDER BY [DONORID] DESC)
IF (@sequence IS NULL OR @sequence = '')
BEGIN
SELECT @sequence = REPLICATE('0', 7 )
END
ELSE
BEGIN
SELECT @tranLen = LEN(@sequence)
SELECT @sequence = @sequence + 1
SELECT @tranLen = ABS(@tranLen - LEN(CAST(@sequence AS INT)))
SELECT @sequence = REPLICATE('0', @tranLen) + @sequence
END
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT
'DN' + REPLICATE('0', ABS(8 - LEN(t.generated_id))) + CAST (t.generated_id as varchar(100)),
t.DONORNAME
FROM
(
SELECT
i.DONORNAME,
@sequence+ROW_NUMBER()OVER(ORDER BY i.DONORNAME) as generated_id
FROM inserted i
) t
所以如果我 运行 下面那两个不同 insert into select
...
INSERT INTO [dbo].[DONORINFO] ([DONORNAME])
SELECT
[NameOfDonor]
FROM [dbo].[_TEMPENDOWMENTFUND] AS ENDF
WHERE [ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME] FROM [dbo].[DONORINFO])
INSERT INTO [dbo].[DONORINFO] ([DONORNAME])
SELECT
[NameOfDonor]
FROM [dbo].[_TEMPENDOWED] AS ENDF
WHERE [ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME] FROM [dbo].[DONORINFO])
donorid
迭代将与这两个不同的 table 来源类似(出于保密原因,我已经省略了捐赠者姓名)。
DONORID DONORNAME (from _TEMPENDOWMENTFUND)
------------------------
DN00000001 test
DN00000002 test
DN00000003 test
DN00000004 test
DN00000005 test
DN00000006 test
DN00000007 test
DONORID DONORNAME (from _TEMPENDOWED)
------------------------
DN00000007 test
DN00000008 test
DN00000009 test
DN00000010 test
DN00000011 test
DN00000012 test
DN00000013 test
这是我的触发器:
ALTER TRIGGER DONORINFO_INSERT
ON [dbo].[DONORINFO] INSTEAD OF INSERT
AS
DECLARE @sequence AS VARCHAR(50) = ''
DECLARE @tranLen VARCHAR(10)
SET @sequence = (SELECT TOP 1 SUBSTRING([DONORID], 3, 8)
FROM [dbo].[DONORINFO]
ORDER BY [DONORID] DESC)
IF (@sequence IS NULL OR @sequence = '')
BEGIN
SELECT @sequence = REPLICATE('0', 7 ) + '1'
END
ELSE
BEGIN
SELECT @tranLen = LEN(@sequence)
SELECT @sequence = @sequence + 1
SELECT @tranLen = ABS(@tranLen - LEN(CAST(@sequence AS INT)))
SELECT @sequence = REPLICATE('0', @tranLen) + @sequence
END
DECLARE @DONORID AS [nvarchar](50) = 'DN' + CONVERT(VARCHAR, @sequence)
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT @DONORID, inserted.DONORNAME
FROM inserted
在脚本的第一行,我正在阅读 DONORINFO
table,我在其中检查了唯一 ID 是否存在。之后,我将把记录插入到那个 table 中。我第一次测试时,insert into select 脚本有效,但第二次时,它失败并发送并违反了主键错误。
但是如果我逐行测试插入,它会起作用。
这是行之有效的逐行插入脚本。
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
VALUES ('DN00000001', 'test')
如果我运行它两次,记录将是这样的:
DONORID DONORNAME
---------------------
DN00000001 test
DN00000002 test
这是对 select 脚本的插入,但不起作用:
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT
'',
[NameOfDonor]
FROM
[dbo].[_TEMPENDOWMENTFUND] AS ENDF
WHERE
[ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME]
FROM [dbo].[DONORINFO])
_TEMPDOWMENTFUND
是我创建的 table,它将存储从 Excel 工作表迁移的数据,触发器的目的是生成一个唯一的 DONORID
对于 DONORINFO
table 上插入的每条记录。
现在我的问题是,我想执行 insert into select 语句,这是一个多行插入,但我很难弄清楚我创建的触发器出了什么问题.
如有任何帮助,我们将不胜感激。谢谢。
将@sequence
完全int
(可能重命名为last_id
),在最后添加字符。
要对行进行编号,请使用 ROW_NUMBER()
in final select from INSERTED
:
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT
'DN' + REPLICATE('0', ABS(@len_you_need - LEN(t.generated_id))) + CAST (t.generated_id as varchar(100)),
t.DONORNAME
FROM
(
SELECT
i.DONORNAME,
@sequence+ROW_NUMBER()OVER(ORDER BY i.DONORNAME) as generated_id
FROM inserted i
) t
@len_you_need
- 是您需要的 DONORID
的长度。我猜这可能是一个8个字符的常量。在您的来源中,您在这里计算:
SELECT @tranLen = LEN(@sequence)
t.rn
是在上面给出的子查询中生成的 "sequence" 值,它有 t
别名。为清楚起见,将其重命名为 generated_id
。
这个区块:
BEGIN
SELECT @tranLen = LEN(@sequence)
SELECT @sequence = @sequence + 1
SELECT @tranLen = ABS(@tranLen - LEN(CAST(@sequence AS INT)))
SELECT @sequence = REPLICATE('0', @tranLen) + @sequence
END
不再需要了。
这是@ivan-starostin 帮我解答的完整解决方案。
ALTER TRIGGER DONORINFO_INSERT ON [dbo].[DONORINFO]
INSTEAD OF INSERT, UPDATE
AS
DECLARE @sequence AS VARCHAR(50) = ''
DECLARE @tranLen VARCHAR(10)
SET @sequence = (SELECT TOP 1 SUBSTRING([DONORID], 3, 8) FROM [dbo].[DONORINFO] ORDER BY [DONORID] DESC)
IF (@sequence IS NULL OR @sequence = '')
BEGIN
SELECT @sequence = REPLICATE('0', 7 )
END
ELSE
BEGIN
SELECT @tranLen = LEN(@sequence)
SELECT @sequence = @sequence + 1
SELECT @tranLen = ABS(@tranLen - LEN(CAST(@sequence AS INT)))
SELECT @sequence = REPLICATE('0', @tranLen) + @sequence
END
INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
SELECT
'DN' + REPLICATE('0', ABS(8 - LEN(t.generated_id))) + CAST (t.generated_id as varchar(100)),
t.DONORNAME
FROM
(
SELECT
i.DONORNAME,
@sequence+ROW_NUMBER()OVER(ORDER BY i.DONORNAME) as generated_id
FROM inserted i
) t
所以如果我 运行 下面那两个不同 insert into select
...
INSERT INTO [dbo].[DONORINFO] ([DONORNAME])
SELECT
[NameOfDonor]
FROM [dbo].[_TEMPENDOWMENTFUND] AS ENDF
WHERE [ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME] FROM [dbo].[DONORINFO])
INSERT INTO [dbo].[DONORINFO] ([DONORNAME])
SELECT
[NameOfDonor]
FROM [dbo].[_TEMPENDOWED] AS ENDF
WHERE [ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME] FROM [dbo].[DONORINFO])
donorid
迭代将与这两个不同的 table 来源类似(出于保密原因,我已经省略了捐赠者姓名)。
DONORID DONORNAME (from _TEMPENDOWMENTFUND)
------------------------
DN00000001 test
DN00000002 test
DN00000003 test
DN00000004 test
DN00000005 test
DN00000006 test
DN00000007 test
DONORID DONORNAME (from _TEMPENDOWED)
------------------------
DN00000007 test
DN00000008 test
DN00000009 test
DN00000010 test
DN00000011 test
DN00000012 test
DN00000013 test