ทริกเกอร์ T-SQL เป็นวิธีที่ทำให้เกิดการเปลี่ยนแปลงที่เกิดขึ้นจริงเท่านั้น?


9

ฉันมีตารางทริกเกอร์ใน UPDATE และ INSERTที่เพิ่มแถวไปยังตารางอื่น จะต้องเพิ่มแถวต่อเมื่อมีการเปลี่ยนแปลงหนึ่งในสี่คอลัมน์ ฉันลองใช้ IF UPDATE (col) เพื่อทดสอบการเปลี่ยนแปลง แต่มีจุดบอด แค่ทดสอบว่ามีค่าบางอย่างเข้ามาฉันต้องไปให้ลึกกว่านี้ฉันต้องเปรียบเทียบค่าเก่ากับค่าใหม่เพื่อดูการเปลี่ยนแปลงที่แท้จริงเกิดขึ้น มันต้องทำงานกับทั้ง INSERT และ UPDATE

ในกรณีของ UPDATE นั้นง่ายเพราะทั้งตารางที่ถูกแทรกและถูกลบมีค่าที่ฉันสามารถเปรียบเทียบได้ภายในทริกเกอร์ อย่างไรก็ตามสำหรับ INSERT เฉพาะตารางแทรกเท่านั้นที่มีค่า เพราะฉันต้องการทั้งหมดนี้ในทริกเกอร์เดียวกันฉันจะจัดการกรณี INSERT ได้อย่างไร

นี่คือสคริปต์ของทริกเกอร์ที่ฉันต้องการแก้ไข:

ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    -- Not all updates require a push
    IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
    BEGIN
        INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
                [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
            )
        SELECT  [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
        FROM inserted 
    END
END

2
คำศัพท์สั้น ๆ เกี่ยวกับการใช้ "IF UPDATE (<column>)" มันจะส่งกลับจริงถ้า DML ระบุค่าสำหรับคอลัมน์โดยไม่คำนึงว่าค่าที่เปลี่ยนแปลงจริงหรือไม่
Jonathan Fite

คำตอบ:


18

คุณสามารถจัดการทั้ง INSERT และ UPDATE ด้วยตัวดำเนินการ EXCEPT EXISTS จะประเมินเป็น TRUE ทั้งคู่หากเป็นเพียง INSERT หรือหากเป็น UPDATE ที่มีค่าต่างกันสำหรับคอลัมน์ใด ๆ เหล่านี้

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...

นี่คือสิ่งที่สวยงามกว่าการดูฟังก์ชั่นการอัพเดทคอลัมน์ต่างๆ เรารวมผู้ที่มีรหัสส่วนหน้าเพื่อส่งค่าที่เปลี่ยนแปลงเท่านั้น (หลังจากการถกเถียงมาก) การใช้ข้อยกเว้นทำให้รู้สึกได้มากขึ้น
Peter Schott

2
ไม่สามารถใช้งานได้ในกรณีที่มีการสลับเป็น 2 แถวในการอัปเดต ถ้าเรามี John Smith สองคนที่ต้องการ JobCodes ที่อัปเดต (John แรกจาก 1 ถึง 2; John สองจาก 2 เป็น 1) - สิ่งนี้จะบอกว่าไม่มีการอัพเดทเกิดขึ้น
สตีเวนฮิบเบิล

2
@StevenHibble - ในขณะที่เป็นไปได้ว่าเป็นไปได้อย่างไรที่จะเกิดขึ้น? กรณีนั้นสามารถแก้ไขได้อย่างง่ายดายโดยการรวมคอลัมน์ PK ในคำสั่ง Select ด้านบน
ชาดเอสเตส

1
ฉันจะบอกว่าความน่าจะเป็นขึ้นอยู่กับแหล่งข้อมูลและความน่าจะเป็นของการป้อนข้อมูลที่ไม่ดี "อ๊ะผิด John Smith ... " ดูเหมือนจะไม่เกิดขึ้น ไม่ว่าในกรณีใด ๆ สิ่งนี้ไม่ได้กล่าวถึงอีกครึ่งหนึ่งของการอัปเดตหลายแถว: คุณจะแน่ใจได้อย่างไรว่าแทรกแถวที่เปลี่ยนแปลงเท่านั้น สิ่งนี้EXISTSจะตรวจสอบว่ามีการเปลี่ยนแปลงแถวใดบ้าง หากคุณเก็บการแทรกไว้จากคำถามคุณจะบันทึกแถวที่อัปเดตทั้งหมดเมื่อมีการเปลี่ยนแปลงเพียงครั้งเดียวด้วยวิธีที่มีความหมาย
สตีเวนฮิบเบิล

2

ในกรณีที่การอัปเดตอาจส่งผลต่อหลายแถวคุณต้องป้องกันสองสิ่ง:

  1. เราต้องการพิจารณาอัปเดตที่สลับค่าระหว่างแถวที่คล้ายกัน หากมี John Smith สองคนที่ต้องการ JobCodes ที่อัปเดต (John แรกจาก 1 ถึง 2; John สองจาก 2 เป็น 1) เราต้องระวังให้ดีเพื่อบอกว่าทั้งสองได้รับการอัปเดตแล้ว
  2. เราต้องการบันทึกแถวที่เปลี่ยนแปลงAT_Person_To_Pushเท่านั้น หากมีการอัปเดต 5 แถว แต่มีการอัปเดตเพียง 2 แถวในแบบที่เราใส่ใจเราจะต้องดำเนินการเฉพาะแถวที่เกี่ยวข้อง 2 แถวเท่านั้น

นี่คือวิธีที่ฉันจะจัดการกับมัน:

  1. ซ้ายเข้าร่วมinsertedไปdeletedเพราะinsertedจะมีแถวสำหรับแทรกและการปรับปรุงในขณะที่deletedจะมีเฉพาะแถวสำหรับการปรับปรุง
  2. ใช้EXISTSกับEXCEPTเพื่อค้นหาแถวที่insertedค่าต่างจากdeletedค่า คุณไม่สามารถใช้งานได้i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...เนื่องจากตารางที่ถูกลบจะว่างเปล่า (และ LEFT JOIN จะคืนค่า null) เมื่อทริกเกอร์จัดการ INSERT
  3. AT_Person_To_Pushใส่เพียงแถวที่ได้รับผลกระทบเข้ามา
ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
            [Facility],
            [VendorID],
            [Person_code],
            [First_Name],
            [Last_Name],
            [JobCode],
            [Alink],
            [Inactive]
        )
    SELECT  i.[Facility],
            i.[VendorID],
            i.[Person_code],
            i.[First_Name],
            i.[Last_Name],
            i.[JobCode],
            i.[Alink],
            i.[Inactive]
    FROM inserted i
         LEFT JOIN deleted d
           ON i.Person_code = d.Person_code
    -- Check for changes that require a push
    WHERE EXISTS (SELECT i.[First_Name], i.[Last_Name], i.[JobCode], i.[Inactive]
                  EXCEPT
                  SELECT d.[First_Name], d.[Last_Name], d.[JobCode], d.[Inactive]);
END

1

ลองนี้

Declare @Acton int=0

If exists (Select 1 from inserted)
set @Acton=1

If exists (Select 1 from deleted)
set @Acton=@Acton+2

if(@Action=1) -- Only insert

if(@Action=3) -- Only Update
begin
IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
Begin

End
end

if(@Action=2) -- Only Delete
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.