2010-01-26 13 views
5

OK. Robię aktualizację w jednym wierszu w tabeli. Wszystkie pola zostaną nadpisane nowymi danymi oprócz klucza podstawowego. Jednak nie wszystkie wartości zmienią b/c aktualizacji. Na przykład, jeśli tabela przedstawia się następująco:T-SQL: co zmieniono COLUMNS po aktualizacji?

TABLE (id int ident, foo varchar(50), bar varchar(50)) 

Wartość początkowa wynosi:

id foo bar 
----------------- 
1 hi there 

I następnie wykonać UPDATE tbl SET foo = 'hi', bar = 'something else' WHERE id = 1

Co chcę wiedzieć, co kolumna była jego wartość zmienione i jaka była jego pierwotna wartość oraz jaka jest jego nowa wartość.

W powyższym przykładzie chciałbym zobaczyć, że kolumna "pasek" została zmieniona z "tam" na "coś innego".

Możliwe bez porównywania kolumn według kolumn? Czy istnieje elegancki komunikat SQL, taki jak EXCEPT, który będzie bardziej szczegółowy niż tylko wiersz?

Dzięki.

Odpowiedz

9

Nie ma specjalnego stwierdzenie można uruchomić, że powie dokładnie, które kolumny zmieniło, ale mimo to zapytanie nie jest trudno napisać:

DECLARE @Updates TABLE 
(
    OldFoo varchar(50), 
    NewFoo varchar(50), 
    OldBar varchar(50), 
    NewBar varchar(50) 
) 

UPDATE FooBars 
SET <some_columns> = <some_values> 
OUTPUT deleted.foo, inserted.foo, deleted.bar, inserted.bar INTO @Updates 
WHERE <some_conditions> 

SELECT * 
FROM @Updates 
WHERE OldFoo != NewFoo 
OR OldBar != NewBar 

Jeśli starasz się zrobić coś rzeczywiście jako w wyniku tych zmian, to najlepiej napisać wyzwalania:

CREATE TRIGGER tr_FooBars_Update 
ON FooBars 
FOR UPDATE AS 
BEGIN 
    IF UPDATE(foo) OR UPDATE(bar) 
     INSERT FooBarChanges (OldFoo, NewFoo, OldBar, NewBar) 
      SELECT d.foo, i.foo, d.bar, i.bar 
      FROM inserted i 
      INNER JOIN deleted d 
       ON i.id = d.id 
      WHERE d.foo <> i.foo 
      OR d.bar <> i.bar 
END 

(Oczywiście, że prawdopodobnie chcesz zrobić więcej niż to w wyzwalacz, ale jest przykładem bardzo uproszczonym działania)

Można użyć COLUMNS_UPDATED zamiast UPDATE ale uważam, że jest to ból, a ona nadal nie powie ci, które kolumny rzeczywiście zmieniły, tylko kolumny, które zostały ujęte w rachunku UPDATE. Na przykład można napisać UPDATE MyTable SET Col1 = Col1 i nadal będzie wiadomo, że Col1 została zaktualizowana, mimo że nie zmieniła się żadna pojedyncza wartość. Pisząc wyzwalacz, musisz przetestować indywidualne wartości przed i po, aby upewnić się, że otrzymujesz prawdziwe zmiany (jeśli tego chcesz).

P.S. Możesz także UNPIVOT, jak mówi Rob, ale nadal będziesz musiał wyraźnie określić kolumny w klauzuli UNPIVOT, to nie jest magia.

+0

Zwyczajowo zostawia się notatkę, gdy się zgłasza, ludzie. Nie bądź palantem, co dokładnie było nie tak z tą odpowiedzią? – Aaronaught

+0

Dzięki za informacje. Szukałem specjalnego oświadczenia, ale jeśli nie ma, myślę, że będę musiał brutalnie zmusić go do działania tak jak powyżej. – Calvin

2

Spróbuj odpakować zarówno wstawione, jak i usunięte, a następnie możesz dołączyć, szukając miejsca, w którym zmieniła się wartość.

+0

Mógłbyś podać przykład przy użyciu powyższym przypadku? – Calvin

+0

Tak, ale teraz jestem na moim iPhonie ... Nieco później, pewnie. –

+1

/* Niestety o formatowaniu, nadzieję, że to działa w porządku */ tworzyć uBlah wyzwalania na dbo.blah po aktualizacji jak wybierz d. *, I.value z ( select id, kol, wartości z usuniętym UNPIVOT (wartość farbownicz w ([bla], [bar])) u ) d przyłączenia ( wybrać identyfikator kol wartość z włożonym UNPIVOT (wartość dla col w ([bla], [bar])) u ) i na d.id = i.id i d.col = i.col i d.value! = i.value ; –

0
OUTPUT deleted.bar AS [OLD VALUE], inserted.bar AS [NEW VALUE] 

@ Calvin Właśnie opierałem się na przykładzie UPDATE. Nie mówię, że to jest pełne rozwiązanie. Ja daje wskazówkę, że można zrobić to gdzieś w kodzie ;-)

Ponieważ ja już mam -1 z powyższej odpowiedzi, pozwól mi rozbić ten w:

Jeśli naprawdę nie wiem która kolumna została zaktualizowana, powiedziałbym utworzyć wyzwalacz i używać COLUMNS_UPDATED function() w organizmie, które powodują (Patrz this)

stworzyłem w moim blog Bitmask Reference do użytku z tym COLUMNS_UPDATED(). To sprawi, że twoje życie łatwiejsze, jeśli użytkownik zdecyduje się podążać tą ścieżką (wyzwalania + Columns_Updated())

Jeśli nie jesteś zaznajomiony z spust, oto mój przykład podstawowej Wyzwalanie http://dbalink.wordpress.com/2008/06/20/how-to-sql-server-trigger-101/

+0

Problem z tym rozwiązaniem polega na tym, że nie wiem, że pole "bar" zmieniło się. W każdym razie dzięki. – Calvin

+0

@Calvin Zobacz moje edycje powyżej ;-) – MarlonRibunal

+1

Więc domyślam się, że jedyne pytanie, które mam do tego jest: Czy COLUMNS_UPDATED(), z odpowiednią maskę bitową mocy (2, fieldnum-1), faktycznie powiedzieć mi, czy wartość zmieniona lub tylko jeśli, jak mówi Aaronaught, to czy pole zostało uwzględnione w instrukcji UPDATE? – Calvin

1

Można to wykryć w sposób wyzwalacz, lub wykorzystać CDC w SQL Server 2008.

Jeśli create a triggerFOR AFTER UPDATE następnie tabela inserted będzie zawierać wiersze z nowymi wartościami, a tabela deleted będzie zawierać odpowiednie wiersze ze starymi wartościami.

+0

Korzystanie z programu SQL Server 2005. Jak dokładnie mógłbym to wykryć w wyzwalaczu? – Calvin

+0

Zobacz moją odpowiedź ;-) – MarlonRibunal

0

Jeśli korzystasz z programu SQL Server 2008, prawdopodobnie powinieneś rzucić okiem na nową funkcję zmiany danych przechwytywania. To zrobi, co chcesz.

+0

CDC naprawdę nie pasuje do tego przypadku użycia. – keithwarren7

+0

Używam programu SQL Server 2005. – Calvin

0

Alternatywną opcją śledzenia zmian danych jest zapisywanie danych w innej (możliwej tymczasowej) tabeli, a następnie analiza różnic przy użyciu XML. Zmienione dane zapisywane są do tabeli kontroli wraz z nazwami kolumn. Tylko jedną rzeczą jest, aby znać pola tabeli, aby przygotować tabelę tymczasową. ten można znaleźć rozwiązanie tutaj: