2012-07-24 15 views
5

Piszę spust, aby śledzić wszystkie zmiany zachodzące w tabeli. Niestety, tabela ma ponad 150 kolumn i chciałem uniknąć zapisywania każdej kolumny w kodzie (np. New.col1, new.col2 ....) i dlatego napisałem następujące zapytanie w "wyzwalaczu po aktualizacji"mysql Wyzwalacz do logowania, znajdź zmienione kolumny

INSERT INTO logs SELECT *, NOW() FROM abc WHERE abc.id = NEW.Id; 

Ten pomysł powoduje wiele problemów z powodu powielania danych, które nie zostały zmienione w zapytaniu o aktualizację.

W skrócie Chcę dynamicznie dowiedzieć się, które kolumny były częścią kwerendy aktualizacji, a jeśli nie jest to możliwe, istnieje sposób na iterację przez wszystkie kolumny "nowego" wiersza, dzięki czemu mogę dynamicznie porównać stare. colName == new. @ colName?

Widziałem już Oracle PL/SQL: Loop Over Trigger Columns Dynamically, How to determine if anything changed in update trigger in t-sql i MySQL UPDATE trigger: INSERTing the values of the columns that actually changed.

Ostatnie łącze jest zamknięte na to, czego potrzebuję z tylko jedną różnicą, nie chcę twardo kodować nazw kolumn w poniższej instrukcji, ponieważ mam sposób ponad 100 kolumn we wszystkich tabelach, które zamierzam napisać podobnie spust na !!

Odpowiedz

2

Tego dnia przeprowadzałem trochę badań i wygląda na to, że natknąłem się na wiele takich samych wyników wyszukiwania, jak Ty. Ostatecznie wydaje mi się, że nie ma sposobu, aby zapętlić wszystkie kolumny tabeli i odwołać się do odpowiednich starych/nowych wartości. Ja osiedleniem się na jawnie sprawdzanie każdej kolumny, a następnie zalogowaniu:

IF (NEW.fld1 <> OLD.fld1) OR (NEW.fld1 IS NOT NULL AND OLD.fld1 IS NULL) OR (NEW.fld1 IS NULL AND OLD.fld1 IS NOT NULL) THEN 
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`) 
VALUES ("tblname", "fld1", OLD.fld1, NEW.fld1); 
END IF; 

IF (NEW.fld2 <> OLD.fld2) OR (NEW.fld2 IS NOT NULL AND OLD.fld2 IS NULL) OR (NEW.fld2 IS NULL AND OLD.fld2 IS NOT NULL) THEN 
INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`) 
VALUES ("tblname", "fld2", OLD.fld2, NEW.fld2); 
END IF; ... 

znalazłem pojęcia o inne rozwiązanie here. Teoretycznie możesz mieć 3 rozgraniczone listy, jedną dla nazw kolumn, jedną dla starych vals i jedną dla nowych vals. Musiałbyś jawnie odwoływać się do starych i nowych valsów, ale byłaby to jedna linia (łatwiejsza do utrzymania lub kopiowania/wklejania w celu implementacji na innych tabelach), a następnie można zapętlić. Tak więc w pseudo-kodzie wyglądałoby to tak:

fields_array = concat_ws(",", "fld1", "fld2"); 
old_vals_array = concat_ws(",", OLD.fld1, OLD.fld2); 
new_vals_array = concat_ws(",", NEW.fld1, NEW.fld2); 

foreach fields_array as key => field_name 
    INSERT INTO `fld_audit` (`table`, `fldname`, `oldval`, `newval`) 
    VALUES ("tblname", field_name, old_vals_array[key], vew_vals_array[key]); 

Nie przemyślałem tego zbyt wiele. Może zajść potrzeba wywołania procedury składowanej zamiast ustawiania zmiennych. Ale warto się nim zajrzeć. Spędziłem już wystarczająco dużo czasu na wyzwalaczach. Nie jestem pewien, czy mógłbym zweryfikować (do mojego szefa) czas prób i błędów w bardziej eleganckim rozwiązaniu.

+0

I zostały obecnie w obliczu nowy rodzaj problemu ... Nawet jeśli jestem gotów ciężko kodem wszystkie moje 150 kolumn w moich wyzwalaczy, mam Trudno jest śledzić kolumnę "UpdatedBy"! Mój oryginalny stół śledzi osoby zalogowane w aplikacji i kto dokonał ostatniej zmiany za pośrednictwem aplikacji. Niestety, nasz dział pomocy technicznej może zmieniać dane bezpośrednio z bazy danych bez aktualizowania kolumny "updatedBy" iw takim przypadku nie ma możliwości, bym wiedział, czy "updatedBy" był częścią zapytania czy nie, i dlatego nie mogę ustawić tej kolumny do null, kiedy zajdzie taka potrzeba! Mam nadzieję, że miałem sens tutaj :) – Sap

+0

To ma sens. Nic nie można z tym zrobić. Właśnie do tego służą interfejsy API i "interfejsy". Aby zmusić "pomoc" ludzi/innych programistów do robienia tego, co powinni. – Jegsar

1

Jak już zasugerowałeś ingratiatednerd, możesz użyć CONCAT_WS do utworzenia ciągów ze wszystkich wymaganych wartości i wykonania pojedynczego porównania.

Być może po to przydatne do kogoś:

DECLARE old_concat, new_concat text; 
SET old_concat = CONCAT_WS(',', OLD.fld1, OLD.fld2, ...); 
SET new_concat = CONCAT_WS(',', NEW.fld1, NEW.fld2, ...); 

IF old_concat <> new_concat 
THEN 
    INSERT STATEMENT 
END IF; 
Powiązane problemy