Bagaimana T-SQL Trigger yang hanya menghasilkan perubahan nyata yang dibuat?

9

Saya memiliki pemicu tabel pada UPDATE dan INSERT yang menambahkan baris ke tabel lain. Hanya perlu menambahkan baris jika salah satu dari empat kolom diubah. Saya mencoba menggunakan JIKA UPDATE (col) untuk menguji perubahan tetapi memiliki blind spot. Ini hanya menguji bahwa beberapa nilai masuk. Saya perlu masuk lebih dalam, saya perlu membandingkan nilai lama dan baru untuk melihat perubahan yang sebenarnya telah terjadi. Itu harus bekerja dengan INSERT dan UPDATE.

Dalam hal PEMBARUAN yang mudah karena tabel yang dimasukkan dan dihapus memiliki nilai yang dapat saya bandingkan di dalam pelatuk. Namun, untuk INSERT hanya tabel sisipan yang memiliki nilai. Karena saya membutuhkan ini semua di pemicu yang sama, bagaimana saya menangani kasus INSERT itu?

Berikut ini skrip pemicu yang ingin saya ubah:

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
WillG
sumber
2
Kata singkat tentang penggunaan "JIKA DIPERBARUI (<kolom>)". Mengembalikan nilai true jika DML menentukan nilai untuk kolom, terlepas dari apakah nilai benar-benar berubah atau tidak.
Jonathan Fite

Jawaban:

18

Anda dapat menangani INSERT dan UPDATE dengan operator set KECUALI. EXISTS hanya akan mengevaluasi TRUE baik jika itu hanya INSERT, atau jika itu adalah UPDATE dengan nilai yang berbeda untuk setiap kolom ini.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
sumber
Ini jauh lebih elegan daripada melihat berbagai fungsi kolom-diperbarui. Kami menggabungkan yang dengan beberapa kode front-end untuk hanya mengirim nilai yang diubah (setelah banyak perselisihan). Menggunakan KECUALI jauh lebih masuk akal.
Peter Schott
2
Ini tidak berfungsi dalam kasus di mana 2 baris "ditukar" dalam pembaruan. Jika kita memiliki dua John Smith yang membutuhkan JobCodes mereka diperbarui (John pertama dari 1 menjadi 2; John kedua dari 2 menjadi 1) - ini akan mengatakan tidak ada pembaruan yang terjadi.
Steven Hibble
2
@ SevenHibble - Sementara mungkin, bagaimana mungkin hal itu terjadi? Kasus itu dapat dengan mudah diatasi dengan memasukkan kolom PK dalam pernyataan Select di atas.
Chad Estes
1
Saya akan mengatakan kemungkinannya tergantung pada sumber data dan kemungkinan entri data yang buruk. "Ups, salah John Smith ..." sepertinya tidak akan pernah terjadi. Bagaimanapun, ini tidak membahas bagian lain dari pembaruan multi-baris: bagaimana Anda memastikan untuk hanya memasukkan baris yang berubah? Ini EXISTSmemeriksa apakah ada baris yang berubah. Jika Anda menyimpan sisipan dari pertanyaan, Anda kemudian akan mencatat semua baris yang diperbarui ketika hanya satu perubahan yang berarti.
Steven Hibble
2

Jika pembaruan dapat memengaruhi banyak baris, Anda harus melindungi dari dua hal:

  1. Kami ingin mempertimbangkan pembaruan yang menukar nilai antara baris yang sama. Jika ada dua John Smith yang memerlukan JobCodes mereka diperbarui (John pertama dari 1 menjadi 2; John kedua dari 2 menjadi 1), kita perlu berhati-hati untuk mengatakan bahwa keduanya diperbarui.
  2. Kami hanya ingin mencatat baris yang diubah AT_Person_To_Push. Jika 5 baris diperbarui, tetapi hanya 2 yang diperbarui dengan cara yang kami pedulikan, maka kami hanya perlu memproses 2 baris yang relevan.

Begini cara saya menanganinya:

  1. Kiri bergabung insertedke deleted, karena insertedakan memiliki baris untuk menyisipkan dan pembaruan sementara deletedhanya akan memiliki baris untuk pembaruan.
  2. Gunakan EXISTSdengan EXCEPTuntuk menemukan baris di mana insertednilai berbeda dari deletednilai. Anda tidak dapat menggunakan i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...karena tabel yang dihapus akan kosong (dan LEFT JOIN akan mengembalikan nol) ketika pemicu menangani INSERT.
  3. Masukkan hanya baris yang terpengaruh ke dalam 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
Steven Hibble
sumber
1

Coba ini,

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
KumarHarsh
sumber