2012-07-24 45 views
5

我正在寫一個觸發器來跟蹤表中發生的所有更改。不幸的是,該表有150列,我想避免寫代碼中的每一列(例如new.col1,new.col2 ....),因此我在「更新觸發器後」中寫下了以下查詢:mysql觸發記錄,找到更改列

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

此想法由於在更新查詢中沒有更改的數據的重複而導致多個問題。

簡而言之,我想動態地找出哪些列是更新查詢的一部分,如果這是不可能的,有沒有一種方法可以遍歷「新」行的所有列,以便我可以動態比較舊。@ colName == new。@ colName?

我已經看到了 Oracle PL/SQL: Loop Over Trigger Columns Dynamically,How to determine if anything changed in update trigger in t-sqlMySQL UPDATE trigger: INSERTing the values of the columns that actually changed

最後一個鏈接是關閉我需要的只有一個區別,我不想在下面的語句中硬編碼列名稱,因爲我有超過100列的方式我將寫所有表中的類似觸發!

IF NEW.column1 <> OLD.column1 THEN INSERT INTO... END IF; IF NEW.column2 <> OLD.column2 THEN INSERT INTO... END IF 

回答

2

今天早上我一直在做這方面的一些研究,看起來像我遇到過很多和你一樣的搜索結果。最終,在我看來,無法遍歷所有表列,並引用相應的舊/新值。我安定在明確檢查每個列,然後登錄:

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; ... 

我找到了另一種解決方案here的端倪。從理論上講,你可以有3個分隔列表,一個用於列名,一個用於舊的vals,另一個用於新的vals。你將不得不明確引用新的和新的vals,但這將是一行(更容易維護或複製/粘貼到其他表上實現),然後你可以循環。因此,在僞代碼中,它看起來像這樣:

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]); 

我沒有想到這一點通過太多。您可能需要調用存儲過程而不是設置變量。但它可能值得深入研究。我已經花了足夠的時間在我的觸發器上。不確定我可以通過更優雅的解決方案驗證(對我的老闆)試錯時間。

+0

我一直正面臨着一個新的問題。即使我已經準備好硬編碼我所有的150列在我的觸發器,我有很難跟蹤「UpdatedBy」列!我的原始表跟蹤登錄到應用程序的人員以及通過應用程序進行最後更改的人員。不幸的是,我們的支持技術可能會直接從數據庫更改數據,而不更新「updatedBy」列,在這種情況下,我無法知道「updatedBy」是否是查詢的一部分,因此我無法設置該列當需要時爲null!我希望我在這裏有意義:) – Sap 2012-08-17 05:06:16

+0

有道理。儘管如此,卻無能爲力。這是API和「接口」的用途。強迫「支持」人員/其他開發人員做他們應該做的事情。 – Jegsar 2015-02-20 04:55:55

1

正如ingratiatednerd已經建議的那樣,您可以使用CONCAT_WS來創建所有需要的字符串並創建一個比較語句。

也許下面是有用的人:

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;