我被問及是否可以跟蹤對MySQL數據庫中記錄的更改。所以當一個領域被改變時,舊的vs新的可用的和發生的日期。有沒有一個功能或共同的技術來做到這一點?是否有MySQL選項/功能來跟蹤記錄更改的歷史記錄?
如果是這樣,我正在考慮做這樣的事情。創建一個名爲更改的TABLE。它將包含與主TABLE相同的字段,但前綴爲舊的和新的,但僅限於實際更改的那些字段以及一個TIMESTAMP。它將被編入一個ID。這樣,可以運行一個SELECT報告來顯示每條記錄的歷史記錄。這是一個好方法嗎?謝謝!
我被問及是否可以跟蹤對MySQL數據庫中記錄的更改。所以當一個領域被改變時,舊的vs新的可用的和發生的日期。有沒有一個功能或共同的技術來做到這一點?是否有MySQL選項/功能來跟蹤記錄更改的歷史記錄?
如果是這樣,我正在考慮做這樣的事情。創建一個名爲更改的TABLE。它將包含與主TABLE相同的字段,但前綴爲舊的和新的,但僅限於實際更改的那些字段以及一個TIMESTAMP。它將被編入一個ID。這樣,可以運行一個SELECT報告來顯示每條記錄的歷史記錄。這是一個好方法嗎?謝謝!
這很微妙。
如果業務需求是「我想審覈對數據的更改 - 誰做了什麼和什麼時間?」,通常可以使用審計表(根據Keethanjan發佈的觸發器示例)。我不是一個觸發器的粉絲,但它具有實現相對無痛的巨大好處 - 您現有的代碼不需要知道觸發器和審計內容。
如果業務需求是「向我顯示過去某個特定日期的數據狀態」,則意味着隨着時間的推移已經進入您的解決方案。雖然你可以通過查看審計表來重建數據庫的狀態,但它很難且容易出錯,而且對於任何複雜的數據庫邏輯來說,它變得很笨拙。例如,如果企業想要知道「找到我們應該發送給在本月的第一天有優秀未付帳單的客戶的地址」,那麼您可能需要翻查六張審計表。相反,您可以將隨時間變化的概念烘焙到您的模式設計中(這是Keethanjan建議的第二種選擇)。這是對應用程序的改變,絕對是在業務邏輯和持久層面上的改變,所以它不是微不足道的。
舉例來說,如果你有一個像這樣的表:
CUSTOMER
---------
CUSTOMER_ID PK
CUSTOMER_NAME
CUSTOMER_ADDRESS
和你想跟蹤隨着時間的推移,你可以按如下修改它:
CUSTOMER
------------
CUSTOMER_ID PK
CUSTOMER_VALID_FROM PK
CUSTOMER_VALID_UNTIL PK
CUSTOMER_STATUS
CUSTOMER_USER
CUSTOMER_NAME
CUSTOMER_ADDRESS
你不想每次都更改客戶記錄,而不是更新記錄,您將當前記錄上的VALID_UNTIL設置爲NOW(),並插入帶有VALID_FROM(現在)和空VALID_UNTIL的新記錄。您將「CUSTOMER_USER」狀態設置爲當前用戶的登錄ID(如果需要保留該狀態)。如果客戶需要刪除,您可以使用CUSTOMER_STATUS標誌來表明這一點 - 您可能永遠不會從該表中刪除記錄。
通過這種方式,您可以隨時查找客戶表在給定日期的狀態 - 地址是什麼?他們改名了嗎?通過加入其他具有類似valid_from和valid_until日期的表,您可以重建歷史上的整個圖片。要查找當前狀態,請搜索VALID_UNTIL日期爲空的記錄。 (嚴格來說,你不需要valid_from,但它使查詢更容易一些)。它使您的設計和數據庫訪問複雜化。但它使重建世界變得更容易。
但是它會爲那些沒有更新的字段添加重複數據?如何管理它? – itzmukeshy7
如果在一段時間內對客戶記錄進行編輯,則難以識別特定條目是屬於同一客戶還是屬於不同客戶,因此報告生成會出現第二種方法問題。 –
最好的建議我已經看到這個問題 – Worthy7
你可以創建觸發器來解決這個問題。 Here is a tutorial to do so(存檔鏈接)。
設置約束和規則在數據庫中比寫 特殊的代碼來處理同樣的任務,因爲它會阻止另一 開發商從寫繞過所有 特殊代碼的不同的查詢更好,可以讓你的數據庫數據完整性較差。
很長一段時間,我使用腳本 將信息複製到另一個表,因爲MySQL當時不支持觸發器。我現在發現這個觸發器在跟蹤一切時更有效。
如果某個人修改了某行,則該觸發器會將舊值複製到歷史記錄表中,如果該值已更改 。每次有人編輯該行時,
Editor ID
和last mod
都存儲在 原始表格中;時間對應於 ,直到它被改變爲當前的形式。
DROP TRIGGER IF EXISTS history_trigger $$
CREATE TRIGGER history_trigger
BEFORE UPDATE ON clients
FOR EACH ROW
BEGIN
IF OLD.first_name != NEW.first_name
THEN
INSERT INTO history_clients
(
client_id ,
col ,
value ,
user_id ,
edit_time
)
VALUES
(
NEW.client_id,
'first_name',
NEW.first_name,
NEW.editor_id,
NEW.last_mod
);
END IF;
IF OLD.last_name != NEW.last_name
THEN
INSERT INTO history_clients
(
client_id ,
col ,
value ,
user_id ,
edit_time
)
VALUES
(
NEW.client_id,
'last_name',
NEW.last_name,
NEW.editor_id,
NEW.last_mod
);
END IF;
END;
$$
另一種解決辦法是保持一個版本域和更新保存這個領域。你可以決定max是最新的版本,或者0是最新的版本。這取決於你。
下面是做到這一點的簡單方法:
首先,創建一個歷史表爲您想要跟蹤(下面的例子查詢),每個數據表。此表將爲每個在數據表中每行執行的插入,更新和刪除查詢都提供一個條目。
歷史表的結構將與其追蹤的數據表相同,除了三個附加列:存儲發生的操作的列(我們稱之爲「操作」),操作的日期和時間,以及存儲序列號('修訂版')的列,該序列號按每個操作遞增,並按數據表的主鍵列進行分組。
要執行此排序行爲,將在主鍵列和修訂列上創建一個雙列(複合)索引。請注意,如果歷史表使用的引擎是MyISAM,則只能按此方式進行排序(See 'MyISAM Notes' on this page)
歷史記錄表相當容易創建。在下面的ALTER TABLE查詢中(以及在下面的觸發器查詢中) ,替換「primary_key_column」在你的數據表中列的實際名稱
CREATE TABLE MyDB.data_history LIKE MyDB.data;
ALTER TABLE MyDB.data_history MODIFY COLUMN primary_key_column int(11) NOT NULL,
DROP PRIMARY KEY, ENGINE = MyISAM, ADD action VARCHAR(8) DEFAULT 'insert' FIRST,
ADD revision INT(6) NOT NULL AUTO_INCREMENT AFTER action,
ADD dt_datetime DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER revision,
ADD PRIMARY KEY (primary_key_column, revision);
,然後創建觸發器:。
DROP TRIGGER IF EXISTS MyDB.data__ai;
DROP TRIGGER IF EXISTS MyDB.data__au;
DROP TRIGGER IF EXISTS MyDB.data__bd;
CREATE TRIGGER MyDB.data__ai AFTER INSERT ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'insert', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__au AFTER UPDATE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'update', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = NEW.primary_key_column;
CREATE TRIGGER MyDB.data__bd BEFORE DELETE ON MyDB.data FOR EACH ROW
INSERT INTO MyDB.data_history SELECT 'delete', NULL, NOW(), d.*
FROM MyDB.data AS d WHERE d.primary_key_column = OLD.primary_key_column;
大功告成現在,所有的刀片,更新和刪除'MyDb.dat一個」將被記錄在‘MyDb.data_history’,給你這樣一個歷史表(減去做作‘data_columns’列)
ID revision action data columns..
1 1 'insert' .... initial entry for row where ID = 1
1 2 'update' .... changes made to row where ID = 1
2 1 'insert' .... initial entry, ID = 2
3 1 'insert' .... initial entry, ID = 3
1 3 'update' .... more changes made to row where ID = 1
3 2 'update' .... changes made to row where ID = 3
2 2 'delete' .... deletion of row where ID = 2
要更新顯示給定列中的變化進行更新,您需要在主鍵和順序列上將自己的歷史表加入自己。你可以創建一個視圖用於此目的,例如:
CREATE VIEW data_history_changes AS
SELECT t2.dt_datetime, t2.action, t1.primary_key_column as 'row id',
IF(t1.a_column = t2.a_column, t1.a_column, CONCAT(t1.a_column, " to ", t2.a_column)) as a_column
FROM MyDB.data_history as t1 INNER join MyDB.data_history as t2 on t1.primary_key_column = t2.primary_key_column
WHERE (t1.revision = 1 AND t2.revision = 1) OR t2.revision = t1.revision+1
ORDER BY t1.primary_key_column ASC, t2.revision ASC
我非常喜歡這個解決方案。但是如果你的主表沒有主鍵,或者你不知道主鍵是什麼,它有點棘手。 – Umingo
哇!這太棒了。完美無瑕的工作! –
由於如何將原始表中的所有索引複製到歷史記錄表(由於CREATE TABLE ... LIKE ....的工作方式),我最近遇到了使用此解決方案的問題。在歷史記錄表上具有唯一索引可能會導致AFTER UPDATE觸發器中的INSERT查詢變爲barf,因此需要將其刪除。 在PHP腳本中,我有這樣做的東西,我查詢新創建的歷史表上的任何唯一索引(「SHOW INDEX FROM data_table WHERE Key_name!='PRIMARY'and Non_unique = 0」),然後刪除它們。 –
下面是我們如何解決它
一個用戶表看起來像這樣
Users
-------------------------------------------------
id | name | address | phone | email | created_on | updated_on
和業務需求發生變化,我們需要檢查用戶以前的所有以前的地址和電話號碼。 新的模式是這樣的
Users (the data that won't change over time)
-------------
id | name
UserData (the data that can change over time and needs to be tracked)
-------------------------------------------------
id | id_user | revision | city | address | phone | email | created_on
1 | 1 | 0 | NY | lake st | 9809 | @long | 2015-10-24 10:24:20
2 | 1 | 2 | Tokyo| lake st | 9809 | @long | 2015-10-24 10:24:20
3 | 1 | 3 | Sdny | lake st | 9809 | @long | 2015-10-24 10:24:20
4 | 2 | 0 | Ankr | lake st | 9809 | @long | 2015-10-24 10:24:20
5 | 2 | 1 | Lond | lake st | 9809 | @long | 2015-10-24 10:24:20
要找到任何用戶的當前地址,我們搜索的UserData與修訂DESC和LIMIT 1
要獲得的時間 一定時期之間的用戶的地址我們可以使用created_on bewteen(date1,date 2)
這樣做的直接方法是在表上創建觸發器。設置一些條件或映射方法。當更新或刪除發生時,它會自動插入「更改」表中。
但最大的部分是如果我們有很多列和大量的表。我們必須輸入每個表的每個列的名稱。顯然,這是浪費時間。
爲了更華麗地處理這個問題,我們可以創建一些程序或函數來檢索列的名稱。
我們也可以使用第三部分的工具來簡單地做到這一點。在這裏,我寫了一個Java程序 Mysql Tracker
只是我2美分。我會創建一個解決方案,記錄改變的內容,與瞬態解決方案非常相似。
我ChangesTable將簡單爲:
DateTime | WhoChanged | TableName | Action | ID |FieldName | OldValue
1)當整個行在主表發生變化,許多條目,將進入這個表,但是這是非常不可能的,所以不是大問題(人們通常只改變一件事) 2)OldVaue(和NewValue,如果你想要的話)必須是某種史詩般的「任何類型」,因爲它可以是任何數據,可能有辦法用RAW類型來做到這一點或者只是使用JSON字符串來轉換進出。
最小的數據使用率,存儲您需要的一切,並且可以一次用於所有表格。我現在正在自己研究這一點,但這可能最終會成爲我走的路。
對於創建和刪除,只需要行ID,不需要字段。在主表上刪除一個標誌(active?)會很好。
的可能的複製[如何保持記錄更新的歷史在MySQL?(http://stackoverflow.com/questions/2536819/ how-to-keep-history-of-record-updates-in-mysql) – Gajus