ทริกเกอร์การปรับปรุง SQL เฉพาะเมื่อมีการแก้ไขคอลัมน์


101

จากการดูตัวอย่างอื่น ๆ ที่ฉันได้พบกับสิ่งต่อไปนี้ แต่ดูเหมือนจะไม่ได้ผลอย่างที่ต้องการ: ฉันต้องการให้อัปเดตข้อมูลที่แก้ไขเฉพาะเมื่อQtyToRepairค่าได้รับการอัปเดตแล้ว ... แต่ไม่เป็นเช่นนั้น ที่.

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

วอลเตอร์

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    UPDATE SCHEDULE SET modified = GETDATE()
        , ModifiedUser = SUSER_NAME()
        , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S
    INNER JOIN Inserted I on S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    WHERE S.QtyToRepair <> I.QtyToRepair
END

8
คำเตือนเกี่ยวกับupdate()- จะทดสอบเฉพาะเมื่อคอลัมน์ปรากฏในรายการอัปเดตและเป็นจริงเสมอสำหรับส่วนแทรก ไม่ได้ตรวจสอบว่าค่าคอลัมน์มีการเปลี่ยนแปลงหรือไม่เนื่องจากคุณอาจมีมากกว่าหนึ่งแถวโดยที่บางค่ามีการเปลี่ยนแปลงและบางค่าไม่มี
Nikola Markovinović

คำตอบ:


128

คุณมีสองทางสำหรับคำถามของคุณ:

1- ใช้คำสั่งอัปเดตในทริกเกอร์ของคุณ

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
        ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END 
END

2- ใช้เข้าร่วมระหว่างตารางที่แทรกและตารางที่ถูกลบ

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;    

    UPDATE SCHEDULE 
    SET modified = GETDATE()
       , ModifiedUser = SUSER_NAME()
       , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S 
    INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber                  
    WHERE S.QtyToRepair <> I.QtyToRepair
    AND D.QtyToRepair <> I.QtyToRepair
END

เมื่อคุณใช้คำสั่ง update สำหรับตารางSCHEDULEและตั้งค่าQtyToRepairคอลัมน์เป็นค่าใหม่ถ้าค่าใหม่เท่ากับค่าเก่าในแถวเดียวหรือหลายแถวโซลูชัน 1 จะอัปเดตแถวที่อัปเดตทั้งหมดในตารางกำหนดการ แต่โซลูชัน 2 จะอัปเดตเฉพาะแถวที่กำหนดเวลาแถวที่ค่าเก่าไม่เท่ากับค่าใหม่ มูลค่า.


ขออภัยที่ไม่ได้ระบุว่าเป็น SQLServer ฉันจำเป็นต้องใช้ตารางที่ถูกลบ ฉันเขียนลงในตารางแยกต่างหาก (เพื่อรักษาประวัติ)
Walter de Jong

9
แนวทาง # 2 จะดีกว่าถ้าคุณไม่ต้องการให้ทริกเกอร์ของคุณเริ่มทำงานหากคอลัมน์มีการเปลี่ยนแปลงเป็นค่าเดียวกัน
Neolisk

2
ฉันคิดว่าฉันอาจจะขาดอะไรไป - แต่วิธี # 1 ไม่ได้ผลเพราะนี่คือหลังการอัปเดตดังนั้นแถวปัจจุบันจะมีค่าเดียวกับแถวแทรกเสมอ คุณต้องเข้าร่วมเพื่อลบหากคุณกำลังใช้งานหลังจากอัปเดต
Rob

5
@Rob คำสั่ง "IF UPDATE" กำลังเรียกใช้โพรซีเดอร์ที่สามารถเข้าถึงรายการคอลัมน์ของคอลัมน์ที่ได้รับการอัปเดตเป็นส่วนหนึ่งของแบบสอบถามดังนั้นจึงไม่จำเป็นต้องทราบค่าก่อนหน้านี้ (เราเพิ่งพบสิ่งนี้ในสภาพแวดล้อมของเราและยืนยันว่า "IF UPDATE" ได้รับการยกย่องโดยไม่คำนึงถึงข้อ "AFTER UPDATE")
TChadwick

3
ทำไมคุณถึงเข้าร่วมinsertedตารางในแบบสอบถามที่สอง มันควรจะเหมือนกับตารางเองไม่ใช่เหรอ?
teran

22

fyi รหัสที่ฉันลงท้ายด้วย:

IF UPDATE (QtyToRepair)
    begin
        INSERT INTO tmpQtyToRepairChanges (OrderNo, PartNumber, ModifiedDate, ModifiedUser, ModifiedHost, QtyToRepairOld, QtyToRepairNew)
        SELECT S.OrderNo, S.PartNumber, GETDATE(), SUSER_NAME(), HOST_NAME(), D.QtyToRepair, I.QtyToRepair FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber 
        WHERE I.QtyToRepair <> D.QtyToRepair
end

1
ฉันพบว่าสิ่งนี้มีประโยชน์เนื่องจาก2- ใช้การเข้าร่วมระหว่างคำตอบด้านบนไม่ได้ผลเนื่องจากเกณฑ์ที่WHERE S.QtyToRepair <> I.QtyToRepair AND D.QtyToRepair <> I.QtyToRepairไม่เคยเริ่มทำงาน / จับคู่เนื่องจากเกณฑ์แรกไม่เคยเป็นจริงตารางที่แทรกตรงกับค่าตารางจริงเสมอ การใช้ "WHERE I.QtyToRepair <> D.QtyToRepair" เป็นกุญแจสำคัญสำหรับฉัน นอกจากนี้ความIF UPDATE (field)ช่วยเหลือจากการยิงทริกเกอร์หลายครั้ง
Firegarden

13

ควรตรวจสอบว่าQtyToRepairมีการอัปเดตในตอนแรกหรือไม่

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
            ON S.OrderNo = I.OrderNo and S.PartNumber =    I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

6

คุณต้องการทำสิ่งต่อไปนี้:

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;

    IF (UPDATE(QtyToRepair))
    BEGIN
        UPDATE SCHEDULE SET modified = GETDATE()
            , ModifiedUser = SUSER_NAME()
            , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo AND S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

โปรดทราบว่าทริกเกอร์นี้จะเริ่มทำงานทุกครั้งที่คุณอัปเดตคอลัมน์ไม่ว่าค่าจะเหมือนกันหรือไม่ก็ตาม


2

เมื่อใดก็ตามที่มีการอัปเดตระเบียนระเบียนจะ "ลบ" นี่คือตัวอย่างของฉัน:

ALTER TRIGGER [dbo].[UpdatePhyDate]
   ON  [dbo].[M_ContractDT1]
   AFTER UPDATE
AS 
BEGIN
    -- on ContarctDT1 PhyQty is updated 
    -- I want system date in Phytate automatically saved
    SET NOCOUNT ON;

    declare @dt1ky as int   

    if(update(Phyqty))
    begin
        select @dt1ky = dt1ky from deleted

        update M_ContractDT1 set PhyDate=GETDATE() where Dt1Ky=  @dt1ky     

    end

END

มันทำงานได้ดี

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