แทรกการปรับปรุงทริกเกอร์วิธีการตรวจสอบว่าแทรกหรือปรับปรุง


162

ฉันต้องเขียนส่วนแทรก, อัปเดตทริกเกอร์ในตาราง A ซึ่งจะลบแถวทั้งหมดออกจากตาราง B ที่มีหนึ่งคอลัมน์ (บอกว่าเรียง) มีค่าเช่นค่าที่แทรก / อัปเดตในคอลัมน์ของตาราง A (พูด Col1) ฉันจะเขียนมันอย่างไรเพื่อให้ฉันสามารถจัดการกับทั้งกรณีอัปเดตและแทรก ฉันจะตรวจสอบว่ามีการดำเนินการทริกเกอร์สำหรับการปรับปรุงหรือแทรก

คำตอบ:


167

ทริกเกอร์มีข้อมูลพิเศษINSERTEDและDELETEDตารางสำหรับติดตาม "ก่อน" และ "หลัง" ข้อมูล ดังนั้นคุณสามารถใช้สิ่งที่ต้องการIF EXISTS (SELECT * FROM DELETED)ตรวจหาการอัปเดต คุณมีเพียงแถวในการDELETEDอัปเดต แต่มีแถวอยู่เสมอINSERTEDแต่มีเสมอแถวใน

ค้นหา "แทรก" ในCREATE TRIGGERเรียก

แก้ไข, 23 พ.ย. 2554

หลังจากความคิดเห็นคำตอบนี้มีไว้สำหรับINSERTEDและเป็นUPDATEDตัวกระตุ้นเท่านั้น
เห็นได้ชัดว่าทริกเกอร์ DELETE ไม่สามารถมี "แถวในเสมอINSERTED" ดังที่ฉันได้กล่าวไว้ข้างต้น


ดูคำตอบของ @ MikeTeeVee ด้านล่างเพื่อรับคำตอบที่สมบูรณ์ อันนี้ไม่สมบูรณ์
Lorenz Meyer

1
@ LorenzMeyer คำถามดั้งเดิมไม่จำเป็นต้องใช้ ฉันยังมี EXISTS (SELECT * FROM DELETED) ไม่แน่ใจว่าทำไมคุณคิดว่ามันยังไม่สมบูรณ์ ...
GBN

อะไร @LorenzMeyer นอกจากนี้ยังอาจจะหมายถึงเป็นคำสั่งว่า " คุณมีเพียงแถวในลบในการปรับปรุง แต่มีเสมอแถวในแทรก. " นี้ไม่ได้เสมอจริงเพราะมีบางครั้งเมื่อมีการอัปเดต / แทรกเรียกว่า Trigger และใส่อยู่ ว่างเปล่า ในคำตอบของฉันฉันอธิบายว่าสิ่งนี้อาจเกิดจากเพรดิเคตกำจัดข้อมูลใด ๆ จากการเปลี่ยนแปลง ในเหตุการณ์นี้ทริกเกอร์ยังคงถูกเรียกใช้สำหรับความพยายาม DML แต่ตาราง DELETED และ INSERTED นั้นว่างเปล่า นี่เป็นเพราะ SQL ยังคงบัญชีสำหรับเวลาที่คุณต้องการบันทึกแต่ละครั้งพยายาม DML (แม้ว่าพวกเขาจะไม่เปลี่ยนแปลงข้อมูลใด ๆ )
MikeTeeVee

127
CREATE TRIGGER dbo.TableName_IUD
ON dbo.TableName
AFTER INSERT, UPDATE, DELETE
AS 
BEGIN
    SET NOCOUNT ON;

    --
    -- Check if this is an INSERT, UPDATE or DELETE Action.
    -- 
    DECLARE @action as char(1);

    SET @action = 'I'; -- Set Action to Insert by default.
    IF EXISTS(SELECT * FROM DELETED)
    BEGIN
        SET @action = 
            CASE
                WHEN EXISTS(SELECT * FROM INSERTED) THEN 'U' -- Set Action to Updated.
                ELSE 'D' -- Set Action to Deleted.       
            END
    END
    ELSE 
        IF NOT EXISTS(SELECT * FROM INSERTED) RETURN; -- Nothing updated or inserted.

    ...

    END

1
ผมชอบที่จะเขียนเลือก 1 แทรกเกินไปในขณะที่ฉันคิดว่ามันส่งสัญญาณความตั้งใจอย่างชัดเจนมากขึ้น แต่ฉันจะ disapointed โดยโปรแกรมเมอร์ MSSQL ว่านี้ทำให้ความแตกต่างในบริบทนี้ใด ๆ ...
Lukáš Lansky

26
IF EXISTS (SELECT * ... ) และ IF EXISTS (SELECT 1) ... มีประสิทธิภาพการทำงานเหมือนกันทุกประการ แถวจะไม่อ่านหรือดึงข้อมูลได้เลย ในความเป็นจริงคุณยังสามารถใช้ IF EXISTS (SELECT 1/0 ... ) ได้และจะยังคงใช้งานได้และจะไม่ทำให้เกิดข้อผิดพลาดหารด้วยศูนย์
Endrju

1
ฉันกำลังสร้าง Triggers แยกต่างหากสำหรับการแทรกอัปเดตและลบตอนนี้คุณสามารถรวมกันได้แล้ว!
UJS

2
หากมีคนเขียนแบบสอบถามลงใน INSERT และ DELETE สองแถวที่แตกต่างกัน (แทรกแถวใหม่และลบแถวอื่นในสคริปต์เดียวกัน) เป็นไปได้หรือไม่ว่าทริกเกอร์ที่ตั้งค่าในแบบด้านบนจะระบุว่าเป็น UPDATE จริง ๆ ไม่ใช่การอัปเดต) เนื่องจากมีข้อมูลในตาราง sql-INSERTED / DELETED หรือไม่
มีนาคม

87

คำแนะนำเหล่านี้จำนวนมากไม่ได้คำนึงถึงหากคุณเรียกใช้คำสั่งลบที่ไม่ลบอะไร
สมมติว่าคุณพยายามลบโดยที่ ID เท่ากับค่าบางอย่างที่ไม่มีอยู่ในตาราง
ทริกเกอร์ของคุณยังคงถูกเรียกใช้ แต่ไม่มีสิ่งใดในตารางที่ถูกลบหรือแทรก

ใช้สิ่งนี้เพื่อความปลอดภัย:

--Determine if this is an INSERT,UPDATE, or DELETE Action or a "failed delete".
DECLARE @Action as char(1);
    SET @Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
                         AND EXISTS(SELECT * FROM DELETED)
                        THEN 'U'  -- Set Action to Updated.
                        WHEN EXISTS(SELECT * FROM INSERTED)
                        THEN 'I'  -- Set Action to Insert.
                        WHEN EXISTS(SELECT * FROM DELETED)
                        THEN 'D'  -- Set Action to Deleted.
                        ELSE NULL -- Skip. It may have been a "failed delete".   
                    END)

ขอขอบคุณเป็นพิเศษสำหรับ @KenDog และ @Net_Prog สำหรับคำตอบของพวกเขา
ฉันสร้างสิ่งนี้จากสคริปต์ของพวกเขา


3
หนึ่งนี้เป็นรางวัลจัดการลบไม่อยู่ การทำงานที่ดี!
Andrew Wolfe

6
เราอาจมี UPDATE ที่ไม่มีผลต่อแถว (หรือแม้แต่ INSERT)
Razvan Socol

@AndrewWolfe คุณกำลังพูดอะไร คำถามเฉพาะกล่าวว่า"ผมต้องการที่จะเขียนแทรกปรับปรุง Trigger บนโต๊ะ" ไม่มีอะไรเกี่ยวกับทริกเกอร์ DELETE
ypercubeᵀᴹ

@ ypercubeᵀᴹขออภัยทริกเกอร์ของฉันประมาณ 80% ครอบคลุมการกำหนดเวลาทั้งหมดสามครั้ง
Andrew Wolfe

18

ฉันใช้สิ่งต่อไปนี้ตรวจพบคำสั่งลบอย่างถูกต้องที่ไม่ลบอะไรเลย:

CREATE TRIGGER dbo.TR_TableName_TriggerName
    ON dbo.TableName
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS(SELECT * FROM INSERTED)
        -- DELETE
        PRINT 'DELETE';
    ELSE
    BEGIN
        IF NOT EXISTS(SELECT * FROM DELETED)
            -- INSERT
            PRINT 'INSERT';
        ELSE
            -- UPDATE
            PRINT 'UPDATE';
    END
END;

4
อันนี้ตรวจพบคำสั่งที่ใส่อะไรไม่ถูกต้องหรือไม่อัพเดทอะไรเลย
Roman Pekar

11

หลังจากการค้นหาจำนวนมากฉันไม่พบตัวอย่างที่แน่นอนของทริกเกอร์ SQL Server เดียวที่จัดการเงื่อนไขการทริกเกอร์ INSERT, UPDATE และ DELETE ทั้งหมด (3) สามเงื่อนไข ในที่สุดฉันก็พบข้อความหนึ่งบรรทัดที่พูดถึงข้อเท็จจริงที่ว่าเมื่อมีการลบหรืออัปเดตเกิดขึ้นตาราง DELETED ทั่วไปจะมีบันทึกสำหรับการกระทำสองอย่างนี้ จากนั้นฉันก็สร้างรูทีนการกระทำเล็ก ๆ ซึ่งกำหนดว่าเหตุใดทริกเกอร์จึงเปิดใช้งาน บางครั้งอินเทอร์เฟซชนิดนี้จำเป็นต้องใช้เมื่อมีทั้งการกำหนดค่าทั่วไปและการกระทำเฉพาะที่จะเกิดขึ้นในทริกเกอร์ INSERT vs. UPDATE ในกรณีเหล่านี้เพื่อสร้างทริกเกอร์แยกต่างหากสำหรับ UPDATE และ INSERT จะกลายเป็นปัญหาการบำรุงรักษา (เช่นทริกเกอร์ทั้งสองถูกอัพเดตอย่างถูกต้องสำหรับการแก้ไขอัลกอริธึมข้อมูลทั่วไปหรือไม่?)

ด้วยเหตุนี้ฉันต้องการให้ข้อมูลโค้ดเหตุการณ์แบบหลายทริกเกอร์ต่อไปนี้สำหรับการจัดการ INSERT, UPDATE, DELETE ในทริกเกอร์หนึ่งสำหรับ Microsoft SQL Server

CREATE TRIGGER [dbo].[INSUPDDEL_MyDataTable]
ON [dbo].[MyDataTable] FOR INSERT, UPDATE, DELETE
AS 

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with caller queries SELECT statements.
-- If an update/insert/delete occurs on the main table, the number of records affected
-- should only be based on that table and not what records the triggers may/may not
-- select.
SET NOCOUNT ON;

--
-- Variables Needed for this Trigger
-- 
DECLARE @PACKLIST_ID varchar(15)
DECLARE @LINE_NO smallint
DECLARE @SHIPPED_QTY decimal(14,4)
DECLARE @CUST_ORDER_ID varchar(15)
--
-- Determine if this is an INSERT,UPDATE, or DELETE Action
-- 
DECLARE @Action as char(1)
DECLARE @Count as int
SET @Action = 'I' -- Set Action to 'I'nsert by default.
SELECT @Count = COUNT(*) FROM DELETED
if @Count > 0
    BEGIN
        SET @Action = 'D' -- Set Action to 'D'eleted.
        SELECT @Count = COUNT(*) FROM INSERTED
        IF @Count > 0
            SET @Action = 'U' -- Set Action to 'U'pdated.
    END

if @Action = 'D'
    -- This is a DELETE Record Action
    --
    BEGIN
        SELECT @PACKLIST_ID =[PACKLIST_ID]
                    ,@LINE_NO = [LINE_NO]
        FROM DELETED

        DELETE [dbo].[MyDataTable]
        WHERE [PACKLIST_ID]=@PACKLIST_ID AND [LINE_NO]=@LINE_NO
    END
 Else
    BEGIN
            --
            -- Table INSERTED is common to both the INSERT, UPDATE trigger
            --
            SELECT @PACKLIST_ID =[PACKLIST_ID]
                ,@LINE_NO = [LINE_NO]
                ,@SHIPPED_QTY =[SHIPPED_QTY]
                ,@CUST_ORDER_ID = [CUST_ORDER_ID]
            FROM INSERTED 

         if @Action = 'I'
            -- This is an Insert Record Action
            --
            BEGIN
                INSERT INTO [MyChildTable]
                    (([PACKLIST_ID]
                    ,[LINE_NO]
                    ,[STATUS]
                VALUES
                    (@PACKLIST_ID
                    ,@LINE_NO
                    ,'New Record'
                    )
            END
        else
            -- This is an Update Record Action
            --
            BEGIN
                UPDATE [MyChildTable]
                    SET [PACKLIST_ID] = @PACKLIST_ID
                          ,[LINE_NO] = @LINE_NO
                          ,[STATUS]='Update Record'
                WHERE [PACKLIST_ID]=@PACKLIST_ID AND [LINE_NO]=@LINE_NO
            END
    END   

9

ฉันเชื่อว่าซ้อน ifs สับสนเล็กน้อยและ:

Flat ดีกว่าซ้อนกัน [The Zen of Python]

;)

DROP TRIGGER IF EXISTS AFTER_MYTABLE

GO

CREATE TRIGGER dbo.AFTER_MYTABLE ON dbo.MYTABLE AFTER INSERT, UPDATE, DELETE 

AS BEGIN 

    --- FILL THE BEGIN/END SECTION FOR YOUR NEEDS.

    SET NOCOUNT ON;

    IF EXISTS(SELECT * FROM INSERTED)  AND EXISTS(SELECT * FROM DELETED) 
        BEGIN PRINT 'UPDATE' END 
    ELSE IF EXISTS(SELECT * FROM INSERTED)  AND NOT EXISTS(SELECT * FROM DELETED) 
        BEGIN PRINT 'INSERT' END 
    ELSE IF    EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
        BEGIN PRINT 'DELETED' END
    ELSE BEGIN PRINT 'NOTHING CHANGED'; RETURN; END  -- NOTHING

END

9
Declare @Type varchar(50)='';
IF EXISTS (SELECT * FROM inserted) and  EXISTS (SELECT * FROM deleted)
BEGIN
    SELECT @Type = 'UPDATE'
END
ELSE IF EXISTS(SELECT * FROM inserted)
BEGIN
    SELECT @Type = 'INSERT'
END
ElSE IF EXISTS(SELECT * FROM deleted)
BEGIN
    SELECT @Type = 'DELETE'
END

5

ลองนี้ ..

ALTER TRIGGER ImportacionesGS ON dbo.Compra 
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN
  -- idCompra is PK
  DECLARE @vIdCompra_Ins INT,@vIdCompra_Del INT
  SELECT @vIdCompra_Ins=Inserted.idCompra FROM Inserted
  SELECT @vIdCompra_Del=Deleted.idCompra FROM Deleted
  IF (@vIdCompra_Ins IS NOT NULL AND @vIdCompra_Del IS NULL)  
  Begin
     -- Todo Insert
  End
  IF (@vIdCompra_Ins IS NOT NULL AND @vIdCompra_Del IS NOT NULL)
  Begin
     -- Todo Update
  End
  IF (@vIdCompra_Ins IS NULL AND @vIdCompra_Del IS NOT NULL)
  Begin
     -- Todo Delete
  End
END

4

ในขณะที่ฉันชอบคำตอบที่ @Alex โพสต์ฉันเสนอรูปแบบนี้กับโซลูชันของ @ Graham ด้านบน

สิ่งนี้ใช้การมีอยู่ของเรกคอร์ดในตาราง INSERTED และ UPDATED โดยเฉพาะซึ่งต่างจากการใช้ COLUMNS_UPDATED สำหรับการทดสอบครั้งแรก นอกจากนี้ยังให้ความช่วยเหลือโปรแกรมเมอร์ที่หวาดระแวงเมื่อรู้ว่าคดีสุดท้ายได้รับการพิจารณา ...

declare @action varchar(4)
    IF EXISTS (SELECT * FROM INSERTED)
        BEGIN
            IF EXISTS (SELECT * FROM DELETED) 
                SET @action = 'U'  -- update
            ELSE
                SET @action = 'I'  --insert
        END
    ELSE IF EXISTS (SELECT * FROM DELETED)
        SET @action = 'D'  -- delete
    else 
        set @action = 'noop' --no records affected
--print @action

คุณจะได้รับ NOOP พร้อมข้อความดังนี้:

update tbl1 set col1='cat' where 1=2

ดูเหมือนว่าคนแรกENDถูกเยื้องอย่างผิดพลาด! (ทำให้เกิดคำถามที่BEGINปิดแรก)
S.Serpooshan

อื่น ๆ หากและสุดท้ายมีคำสั่งเดียว การเริ่มต้นและสิ้นสุดนั้นไม่จำเป็นจริง ๆ เนื่องจาก IF / Else เป็นคำสั่งเดียว ฉันแก้ไขการเยื้องแล้ว ขอบคุณสำหรับความช่วยเหลือ
เกร็ก

3

นี่อาจเป็นวิธีที่เร็วกว่า:

DECLARE @action char(1)

IF COLUMNS_UPDATED() > 0 -- insert or update
BEGIN
    IF EXISTS (SELECT * FROM DELETED) -- update
        SET @action = 'U'
    ELSE
        SET @action = 'I'
    END
ELSE -- delete
    SET @action = 'D'

4
วิธีนี้ใช้ไม่ได้กับตารางที่มีคอลัมน์จำนวนมากเนื่องจาก column_updated () ส่งคืนค่า varbinary ซึ่งมีค่ามาก ดังนั้น "> 0" จึงล้มเหลวเนื่องจากค่าเริ่มต้น 0 ไปยังหมายเลขที่เก็บไว้ภายในมีค่าน้อยกว่าค่าที่ส่งคืนจาก column_updated ()
Graham

3

ปัญหาที่อาจเกิดขึ้นกับวิธีแก้ไขปัญหาที่สองที่นำเสนอนั้นขึ้นอยู่กับวิธีการเขียนแบบสอบถามการปรับปรุงอาจปรับปรุงศูนย์ระเบียนและแบบสอบถามแทรกอาจแทรกศูนย์ระเบียน ในกรณีเหล่านี้ชุดระเบียนที่แทรกและถูกลบจะว่างเปล่า ในหลายกรณีหากทั้งชุดระเบียนที่ถูกแทรกและถูกลบว่างเปล่าคุณอาจต้องการออกจากทริกเกอร์โดยไม่ทำอะไรเลย


2

ฉันพบข้อผิดพลาดเล็กน้อยใน Grahams วิธีแก้ปัญหาที่ยอดเยี่ยม:

ควรเป็นถ้า COLUMNS_UPDATED () < > 0 - แทรกหรืออัปเดต
แทน> 0 อาจเป็นเพราะบิตด้านบนได้รับการตีความว่าเป็นบิตจำนวนเต็มลงชื่อ SIGNED ... (?) ดังนั้นทั้งหมด:

DECLARE @action CHAR(8)  
IF COLUMNS_UPDATED() <> 0 -- delete or update?
BEGIN     
  IF EXISTS (SELECT * FROM deleted) -- updated cols + old rows means action=update       
    SET @action = 'UPDATE'     
  ELSE
    SET @action = 'INSERT' -- updated columns and nothing deleted means action=insert
END 
ELSE -- delete     
BEGIN
  SET @action = 'DELETE'
END

1

นี้ไม่หลอกลวงสำหรับฉัน:

declare @action_type int;
select @action_type = case
                       when i.id is not null and d.id is     null then 1 -- insert
                       when i.id is not null and d.id is not null then 2 -- update
                       when i.id is     null and d.id is not null then 3 -- delete
                     end
  from      inserted i
  full join deleted  d on d.id = i.id

เนื่องจากไม่ใช่คอลัมน์ทั้งหมดที่สามารถอัปเดตได้ในแต่ละครั้งคุณจึงสามารถตรวจสอบได้ว่าคอลัมน์ใดคอลัมน์หนึ่งกำลังได้รับการอัปเดตด้วยสิ่งต่อไปนี้:

IF UPDATE([column_name])

ความท้าทายที่มีทางออกนี้คือคุณต้องรู้ชื่อคอลัมน์ บางส่วนของคนอื่น ๆ ได้รับการออกแบบดังกล่าวที่คุณก็สามารถคัดลอกวางจากห้องสมุดข้อมูลเท่านั้น ประเด็นเล็ก ๆ แต่การพิจารณาทุกสิ่งโซลูชันทั่วไปนั้นดีกว่าโซลูชันเฉพาะกรณี IMHO
greg

1
declare @insCount int
declare @delCount int
declare @action char(1)

select @insCount = count(*) from INSERTED
select @delCount = count(*) from DELETED

    if(@insCount > 0 or @delCount > 0)--if something was actually affected, otherwise do nothing
    Begin
        if(@insCount = @delCount)
            set @action = 'U'--is update
        else if(@insCount > 0)
            set @action = 'I' --is insert
        else
            set @action = 'D' --is delete

        --do stuff here
    End

1
ฉันจะไม่ใช้ COUNT (*) เพื่อเหตุผลด้านประสิทธิภาพ - มันต้องสแกนทั้งตาราง ฉันจะตั้งแทนธงโดยใช้ IF EXISTS (* เลือกจากแทรก) เดียวกันที่ถูกลบ ฉันรู้ว่าปกติจะมีผลกระทบเพียงไม่กี่แถว แต่ทำไมระบบจึงช้าลง
Endrju

ฉันกำลังจะโพสต์สิ่งที่คล้ายกันมากในการแก้ปัญหา มันช่างพูดมาก แต่อ่านง่ายมาก การแลกเปลี่ยนที่เป็นธรรม ฉันยังชอบวิธีแก้ปัญหาของ Grahms ด้านบน
greg

1

ฉันชอบโซลูชันที่เป็น "วิทยาศาสตร์คอมพิวเตอร์ที่สง่างาม" วิธีการแก้ปัญหาของฉันที่นี่ฮิต pseudotables [แทรก] และ [ลบ] หนึ่งครั้งเพื่อรับสถานะของพวกเขาและทำให้ผลในตัวแปรบิตแมป จากนั้นแต่ละชุดค่าผสมที่เป็นไปได้ของ INSERT, UPDATE และ DELETE สามารถทดสอบได้อย่างง่ายดายตลอดทั้งทริกเกอร์ด้วยการประเมินไบนารีที่มีประสิทธิภาพ (ยกเว้นชุด INSERT หรือ DELETE ที่ไม่น่าจะเกิดขึ้น)

มันทำให้สมมติฐานที่ว่ามันไม่สำคัญว่าคำสั่ง DML คืออะไรหากไม่มีการแก้ไขแถว (ซึ่งควรจะตอบสนองต่อกรณีส่วนใหญ่) ดังนั้นแม้จะไม่สมบูรณ์เท่ากับโซลูชันของ Roman Pekar แต่ก็มีประสิทธิภาพมากกว่า

ด้วยวิธีนี้เรามีความเป็นไปได้ของทริกเกอร์ "สำหรับ INSERT, UPDATE, DELETE" หนึ่งรายการต่อหนึ่งตารางทำให้เรา A) ควบคุมลำดับการดำเนินการได้อย่างสมบูรณ์และ b) การใช้โค้ดหนึ่งรหัสต่อการกระทำที่ใช้งานได้หลายอย่าง (เห็นได้ชัดว่าทุกรูปแบบการใช้งานมีข้อดีและข้อเสียของมันคุณจะต้องประเมินระบบของคุณเป็นรายบุคคลสำหรับสิ่งที่ดีที่สุดจริงๆ)

หมายเหตุว่า "มีอยู่ (* เลือกจาก«แทรก / ถูกลบ»)" งบมีประสิทธิภาพมากเนื่องจากไม่มีการเข้าถึงดิสก์ ( https://social.msdn.microsoft.com/Forums/en-US/01744422-23fe-42f6 -9ab0-a255cdf2904a )

use tempdb
;
create table dbo.TrigAction (asdf int)
;
GO
create trigger dbo.TrigActionTrig
on dbo.TrigAction
for INSERT, UPDATE, DELETE
as
declare @Action tinyint
;
-- Create bit map in @Action using bitwise OR "|"
set @Action = (-- 1: INSERT, 2: DELETE, 3: UPDATE, 0: No Rows Modified 
  (select case when exists (select * from inserted) then 1 else 0 end)
| (select case when exists (select * from deleted ) then 2 else 0 end))
;
-- 21 <- Binary bit values
-- 00 -> No Rows Modified
-- 01 -> INSERT -- INSERT and UPDATE have the 1 bit set
-- 11 -> UPDATE <
-- 10 -> DELETE -- DELETE and UPDATE have the 2 bit set

raiserror(N'@Action = %d', 10, 1, @Action) with nowait
;
if (@Action = 0) raiserror(N'No Data Modified.', 10, 1) with nowait
;
-- do things for INSERT only
if (@Action = 1) raiserror(N'Only for INSERT.', 10, 1) with nowait
;
-- do things for UPDATE only
if (@Action = 3) raiserror(N'Only for UPDATE.', 10, 1) with nowait
;
-- do things for DELETE only
if (@Action = 2) raiserror(N'Only for DELETE.', 10, 1) with nowait
;
-- do things for INSERT or UPDATE
if (@Action & 1 = 1) raiserror(N'For INSERT or UPDATE.', 10, 1) with nowait
;
-- do things for UPDATE or DELETE
if (@Action & 2 = 2) raiserror(N'For UPDATE or DELETE.', 10, 1) with nowait
;
-- do things for INSERT or DELETE (unlikely)
if (@Action in (1,2)) raiserror(N'For INSERT or DELETE.', 10, 1) with nowait
-- if already "return" on @Action = 0, then use @Action < 3 for INSERT or DELETE
;
GO

set nocount on;

raiserror(N'
INSERT 0...', 10, 1) with nowait;
insert dbo.TrigAction (asdf) select top 0 object_id from sys.objects;

raiserror(N'
INSERT 3...', 10, 1) with nowait;
insert dbo.TrigAction (asdf) select top 3 object_id from sys.objects;

raiserror(N'
UPDATE 0...', 10, 1) with nowait;
update t set asdf = asdf /1 from dbo.TrigAction t where asdf <> asdf;

raiserror(N'
UPDATE 3...', 10, 1) with nowait;
update t set asdf = asdf /1 from dbo.TrigAction t;

raiserror(N'
DELETE 0...', 10, 1) with nowait;
delete t from dbo.TrigAction t where asdf < 0;

raiserror(N'
DELETE 3...', 10, 1) with nowait;
delete t from dbo.TrigAction t;
GO

drop table dbo.TrigAction
;
GO

ขอบคุณสำหรับการแก้ปัญหานี้ที่ feets ในบริบทของฉัน คุณจะแนะนำวิธีปรับปรุงคอลัมน์ LastUpdated ของแถวที่ปรับปรุง / แทรกหรือไม่ คุณจะแนะนำวิธีการจัดเก็บ ID ของแถวที่ถูกลบในตารางอื่น (อาจเป็นรหัสที่ประกอบด้วย) หรือไม่
Sébastien

0

แก้ปัญหาด่วน MySQL

โดยวิธีการ: ผมใช้ MySQL PDO

(1) ในตารางการเพิ่มอัตโนมัติเพียงรับค่าสูงสุด (คอลัมน์ของฉัน = id) จากคอลัมน์ที่เพิ่มขึ้นเมื่อสคริปต์ทุกตัวรันก่อน:

$select = "
    SELECT  MAX(id) AS maxid
    FROM    [tablename]
    LIMIT   1
";

(2) เรียกใช้แบบสอบถาม MySQL ตามที่คุณต้องการและแปลงผลลัพธ์เป็นจำนวนเต็มเช่น:

$iMaxId = (int) $result[0]->maxid;

(3) หลังจากแบบสอบถาม "INSERT INTO ... ON DUPLICATE KEY UPDATE" จะได้รับ ID ที่แทรกล่าสุดตามที่คุณต้องการเช่น:

$iLastInsertId = (int) $db->lastInsertId();

(4) เปรียบเทียบและตอบสนอง: หาก lastInsertId สูงกว่าสูงสุดในตารางนั่นอาจเป็น INSERT ใช่ไหม และในทางกลับกัน.

if ($iLastInsertId > $iMaxObjektId) {
    // IT'S AN INSERT
}
else {
    // IT'S AN UPDATE
}

ฉันรู้ว่ามันเร็วและอาจสกปรก และมันเป็นโพสต์เก่า แต่เดี๋ยวก่อนฉันกำลังค้นหาวิธีแก้ปัญหาเป็นเวลานานและบางคนอาจพบว่าวิธีของฉันค่อนข้างมีประโยชน์อยู่แล้ว ดีที่สุด!


0

วิธีง่ายๆ

CREATE TRIGGER [dbo].[WO_EXECUTION_TRIU_RECORD] ON [dbo].[WO_EXECUTION]
WITH EXECUTE AS CALLER
FOR INSERT, UPDATE
AS
BEGIN  

  select @vars = [column] from inserted 
  IF UPDATE([column]) BEGIN
    -- do update action base on @vars 
  END ELSE BEGIN
    -- do insert action base on @vars 
  END

END 

ตาม SSMS IDE ของฉันไวยากรณ์ของคุณไม่ถูกต้องกับวิธีการที่คุณห่อตรรกะของคุณใน IF BEGIN - END ELSE BEGIN - END block
Erutan409

0

ในสถานการณ์แรกฉันคิดว่าตารางของคุณมีคอลัมน์ประจำตัว

CREATE TRIGGER [dbo].[insupddel_yourTable] ON [yourTable]
FOR INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE @action nvarchar(10)
SELECT @action = CASE WHEN COUNT(i.Id) > COUNT(d.Id) THEN 'inserted'
                      WHEN COUNT(i.Id) < COUNT(d.Id) THEN 'deleted' ELSE 'updated' END
FROM inserted i FULL JOIN deleted d ON i.Id = d.Id

ในสถานการณ์ที่สองไม่จำเป็นต้องใช้คอลัมน์ IDENTITTY

CREATE TRIGGER [dbo].[insupddel_yourTable] ON [yourTable]
FOR INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE @action nvarchar(10),
        @insCount int = (SELECT COUNT(*) FROM inserted),
        @delCount int = (SELECT COUNT(*) FROM deleted)
SELECT @action = CASE WHEN @insCount > @delCount THEN 'inserted'
                      WHEN @insCount < @delCount THEN 'deleted' ELSE 'updated' END

ฉันมีปัญหาเดียวกันบางคนสามารถช่วยฉันได้ ดูลิงค์ต่อไปนี้stackoverflow.com/questions/26043106/…
Ramesh S

0
DECLARE @INSERTEDCOUNT INT,
        @DELETEDCOUNT INT

SELECT @INSERTEDCOUNT = COUNT([YourColumnName]) FROM inserted

SELECT @DELETEDCOUNT = COUNT([YourColumnName]) FROM deleted

ถ้าเป็นการอัพเดท

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

ถ้ามันแทรก

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0

0

ฉันใช้การexists (select * from inserted/deleted)ค้นหาเหล่านั้นเป็นเวลานาน แต่ก็ยังไม่เพียงพอสำหรับการดำเนินการ CRUD ที่ว่างเปล่า (เมื่อไม่มีระเบียนในinsertedและdeletedตาราง) ดังนั้นหลังจากค้นคว้าหัวข้อนี้นิดหน่อยฉันก็ได้พบวิธีแก้ปัญหาที่แม่นยำกว่านี้แล้ว:

declare
    @columns_count int = ?? -- number of columns in the table,
    @columns_updated_count int = 0

-- this is kind of long way to get number of actually updated columns
-- from columns_updated() mask, it's better to create helper table
-- or at least function in the real system
with cte_columns as (
    select @columns_count as n
    union all
    select n - 1 from cte_columns where n > 1
), cte_bitmasks as (
    select
        n,
        (n - 1) / 8 + 1 as byte_number,
        power(2, (n - 1) % 8) as bit_mask
    from cte_columns
)
select
    @columns_updated_count = count(*)
from cte_bitmasks as c
where
    convert(varbinary(1), substring(@columns_updated_mask, c.byte_number, 1)) & c.bit_mask > 0

-- actual check
if exists (select * from inserted)
    if exists (select * from deleted)
        select @operation = 'U'
    else
        select @operation = 'I'
else if exists (select * from deleted)
    select @operation = 'D'
else if @columns_updated_count = @columns_count
    select @operation = 'I'
else if @columns_updated_count > 0
    select @operation = 'U'
else
    select @operation = 'D'

เป็นไปได้ที่จะใช้columns_updated() & power(2, column_id - 1) > 0เพื่อดูว่าคอลัมน์ได้รับการอัปเดตหรือไม่ แต่ไม่ปลอดภัยสำหรับตารางที่มีคอลัมน์จำนวนมาก ฉันใช้วิธีการคำนวณที่ซับซ้อนเล็กน้อย (ดูบทความที่เป็นประโยชน์ด้านล่าง)

นอกจากนี้วิธีการนี้จะยังคงจัดประเภทการปรับปรุงบางอย่างไม่ถูกต้องเป็นส่วนแทรก (หากทุกคอลัมน์ในตารางได้รับผลกระทบจากการปรับปรุง) และอาจจะจัดประเภทแทรกที่มีค่าเริ่มต้นเท่านั้นที่แทรกเป็นลบ แต่สิ่งเหล่านี้ ให้เช่าในระบบของฉัน) นอกจากนั้นฉันยังไม่รู้วิธีปรับปรุงโซลูชันนี้ในขณะนี้


0
declare @result as smallint
declare @delete as smallint = 2
declare @insert as smallint = 4
declare @update as smallint = 6
SELECT @result = POWER(2*(SELECT count(*) from deleted),1) + POWER(2*(SELECT 
     count(*) from inserted),2)

if (@result & @update = @update) 
BEGIN
  print 'update'
  SET @result=0
END
if (@result & @delete = @delete)
  print 'delete'
if (@result & @insert = @insert)
  print 'insert'

0

ฉันทำนี่:

select isnull((select top 1 1 from inserted t1),0) + isnull((select top 1 2 from deleted t1),0)

1 -> การแทรก

2 -> ลบ

3 -> อัปเดต

set @i = isnull((select top 1 1 from inserted t1),0) + isnull((select top 1 2 from deleted t1),0)
--select @i

declare @action varchar(1) = case @i when 1 then 'I' when 2 then 'D' when 3 then 'U' end
--select @action


select @action c1,* from inserted t1 where @i in (1,3) union all
select @action c1,* from deleted t1 where @i in (2)

0
DECLARE @ActionType CHAR(6);
SELECT  @ActionType = COALESCE(CASE WHEN EXISTS(SELECT * FROM INSERTED)
                                     AND EXISTS(SELECT * FROM DELETED)  THEN 'UPDATE' END,
                               CASE WHEN EXISTS(SELECT * FROM DELETED)  THEN 'DELETE' END,
                               CASE WHEN EXISTS(SELECT * FROM INSERTED) THEN 'INSERT' END);
PRINT   @ActionType;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.