简体   繁体   中英

Cannot perform multi-row insert on trigger when inserting record with unique id

Here is my trigger:

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

In the first lines of the script, I'm reading the DONORINFO table in which I checked if the unique id exists. After that, I will insert the record into that table. I tested the first time, the insert into select script works but for the second time around, it fails and sends and a violation of primary key error.

But if I tested row by row insert, it works.

This is the row by row insert script that works.

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

If I run it twice, the records will be like this:

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

This is the insert into select script that doesn't work:

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

The _TEMPDOWMENTFUND is a table I created that will store the data that was migrated from an Excel worksheet, the purpose of the trigger is that it will generate a unique DONORID for every record inserted on the DONORINFO table.

Now my problem is that, I want to perform the insert into select statement which is a multiple row insert, but I'm having a hard time figuring out what is going wrong to the trigger I created.

Any help would be appreciated. Thanks.

Make @sequence totally int (and probably rename it to last_id ), add characters in the very end.

To number rows use 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 - is the length of DONORID you need. I guess this may be a constant of 8 characters. In your source you are calculating this here:

SELECT @tranLen = LEN(@sequence)

t.rn is a "sequence" value generated in subquery given above, which has t alias. Renamed it to generated_id for clarity.

This block:

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 

is unnecessary anymore.

Here is the complete solution @ivan-starostin helped me answer.

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

So if I ran those two different insert into select below...

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])

The donorid iteration will be something like this with these two different table sources (I already omitted donor names because of confidentiality).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM