插入具有唯一 ID 的记录时,无法在触发器上执行多行插入

Posted

技术标签:

【中文标题】插入具有唯一 ID 的记录时,无法在触发器上执行多行插入【英文标题】:Cannot perform multi-row insert on trigger when inserting record with unique id 【发布时间】:2016-06-30 09:08:42 【问题描述】:

这是我的触发器:

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 表,在该表中我检查了唯一ID 是否存在。之后,我会将记录插入到该表中。我第一次测试,insert into select 脚本工作,但第二次,它失败并发送和违反主键错误。

但如果我逐行测试插入,它就可以工作。

这是有效的逐行插入脚本。

INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
VALUES ('DN00000001', 'test')

如果我运行两次,记录会是这样的:

DONORID     DONORNAME   
---------------------
DN00000001  test    
DN00000002  test    

这是插入到选择脚本中不起作用:

INSERT INTO [dbo].[DONORINFO] ([DONORID], [DONORNAME])
    SELECT
        '',
        [NameOfDonor] 
    FROM 
        [dbo].[_TEMPENDOWMENTFUND] AS ENDF
    WHERE 
        [ENDF].[NameOfDonor] NOT IN (SELECT [DONORNAME] 
                                     FROM [dbo].[DONORINFO])

_TEMPDOWMENTFUND 是我创建的一个表,它将存储从 Excel 工作表迁移的数据,触发器的目的是它将为插入到 DONORINFO 表中的每条记录生成一个唯一的 DONORID .

现在我的问题是,我想执行 insert into select 语句,这是一个多行插入,但我很难弄清楚我创建的触发器出了什么问题。

任何帮助将不胜感激。谢谢。

【问题讨论】:

【参考方案1】:

@sequence 完全设为int(并可能将其重命名为last_id),最后添加字符。

要对行进行编号,请在从INSERTED 的最终选择中使用ROW_NUMBER()

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 是在上面给出的子查询中生成的“序列”值,具有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 

不再需要了。

【讨论】:

嗨,我似乎无法让它工作,你能把整个脚本贴出来让我测试一下吗? @japogs007ph 不,你应该自己努力。 “整个脚本” - 是您的触发脚本。如果您“无法使其工作” - 使用您当前的问题和当前代码更新您的问题。以标题# UPD Code with ROWNUMBER 开头 我已经尝试过你的脚本部分,但我不明白从哪里获得@len_tou_needt.rn 的值。我对创建触发器有点陌生,对此我深表歉意。我已经尝试过研究上述问题,这就是我最终在这里提出问题的原因。 我尝试了您的解决方案并让它工作。我仍然包含了不需要使其适用于我的场景的块【参考方案2】:

这是@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 迭代将与这两个不同的表源类似(由于机密性,我已经省略了捐助者姓名)。

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

【讨论】:

以上是关于插入具有唯一 ID 的记录时,无法在触发器上执行多行插入的主要内容,如果未能解决你的问题,请参考以下文章

MySQL触发器将日期插入具有唯一匹配ID的行

Mysql唯一索引线上故障记录

从oracle中的触发器插入多条记录

mysql 在插入数据时,怎么样做到自动生成16位唯一的ID?

在具有聚集列存储索引的表上创建触发器 - 错误

在sqlalchemy中插入具有一对多关系的新记录