İşte bunu yapmanın basit bir yolu:
Öncelikle, izlemek istediğiniz her veri tablosu için bir geçmiş tablosu oluşturun (aşağıdaki örnek sorgu). Bu tablo, veri tablosundaki her satırda gerçekleştirilen her ekleme, güncelleme ve silme sorgusu için bir girişe sahip olacaktır.
Geçmiş tablosunun yapısı, üç ek sütun dışında izlediği veri tablosu ile aynı olacaktır: gerçekleşen işlemi saklamak için bir sütun (buna 'eylem' diyelim), işlemin tarihi ve saati ve bir sütun İşlem başına artan ve veri tablosunun birincil anahtar sütununa göre gruplanan bir sıra numarası ('revizyon') saklamak için.
Bu sıralama davranışını gerçekleştirmek için, birincil anahtar sütununda ve revizyon sütununda iki sütunlu (bileşik) bir dizin oluşturulur. Geçmiş tablosu tarafından kullanılan motor MyISAM ise yalnızca bu şekilde sıralama yapabileceğinizi unutmayın ( Bu sayfadaki 'MyISAM Notları'na bakın)
Geçmiş tablosunun oluşturulması oldukça kolaydır. Aşağıdaki ALTER TABLE sorgusunda (ve bunun altındaki tetikleme sorgularında), veri tablonuzdaki 'birincil_anahtar_sütun'u o sütunun gerçek adıyla değiştirin.
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);
Ve sonra tetikleyicileri yaratırsınız:
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;
Ve bitirdiniz. Şimdi, 'MyDb.data'daki tüm ekler, güncellemeler ve silmeler,' MyDb.data_history'ye kaydedilecek ve size bunun gibi bir geçmiş tablosu verecek (çıkarılmış 'data_columns' sütunu)
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
Güncellemeden güncellemeye kadar belirli bir sütun veya sütunlar için değişiklikleri görüntülemek için, geçmiş tablosunu birincil anahtar ve sıra sütunlarında kendisine eklemeniz gerekir. Bu amaçla bir görünüm oluşturabilirsiniz, örneğin:
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
Düzenleme: Vay canına, insanlar 6 yıl önceki tarih masamı seviyorlar: P
Bunu uygulamam hala mırıldanıyor, daha da büyüyor ve daha hantal hale geliyor. Bu veritabanındaki geçmişe bakmak için görüşler ve oldukça güzel bir kullanıcı arayüzü yazdım, ancak bunun çok fazla kullanıldığını sanmıyorum. O zaman o gider.
Bazı yorumları belirli bir sırayla ele almak için:
PHP'de biraz daha karmaşık olan kendi uygulamamı yaptım ve yorumlarda açıklanan bazı sorunlardan kaçındım (önemli ölçüde dizinlerin aktarılması. Benzersiz dizinler üzerinden geçmiş tablosuna aktarırsanız işler bozulacak. Çözümler var. bu yorumlarda). Bu yazıyı mektuba takip etmek, veritabanınızın ne kadar yerleşik olduğuna bağlı olarak bir macera olabilir.
Birincil anahtar ile revizyon sütunu arasındaki ilişki kapalı görünüyorsa, bu genellikle bileşik anahtarın bir şekilde çalıştırıldığı anlamına gelir. Birkaç ender olayda bu oldu ve nedenini kaybettim.
Tetikleyicileri olduğu gibi kullanarak bu çözümü oldukça performanslı buldum. Ayrıca MyISAM, tüm tetikleyicilerin yaptığı gibi eklerde hızlıdır. Bunu akıllı indeksleme (veya eksiklik ...) ile daha da iyileştirebilirsiniz. Bir MyISAM tablosuna birincil anahtarla tek bir satır eklemek, başka bir yerde önemli sorunlarınız olmadıkça, gerçekten optimize etmeniz gereken bir işlem olmamalıdır. MySQL veritabanını çalıştırdığım süre boyunca bu geçmiş tablosu uygulaması açıktı, hiçbir zaman ortaya çıkan (birçok) performans sorununun nedeni olmadı.
Tekrarlanan eklemeler alıyorsanız, INSERT IGNORE türü sorgular için yazılım katmanınızı kontrol edin. Hrmm, şimdi hatırlayamıyorum, ancak bu şema ve işlemlerde birden çok DML eylemi çalıştırdıktan sonra sonuçta başarısız olan sorunlar olduğunu düşünüyorum. En azından farkında olunması gereken bir şey.
Geçmiş tablosundaki ve veri tablosundaki alanların eşleşmesi önemlidir. Veya, veri tablonuzun geçmiş tablosundan DAHA FAZLA sütuna sahip olmamasıdır. Aksi takdirde, geçmiş tablolarına yapılan eklemeler sorguya var olmayan sütunlar koyduğunda (tetikleme sorgularındaki d. * Nedeniyle) ve tetikleyici başarısız olduğunda, veri tablosundaki ekleme / güncelleme / silme sorguları başarısız olur. MySQL, veri tablosuna sütunlar eklenmişse geçmiş tablosunu değiştirebileceğiniz şema tetikleyicileri gibi bir şeye sahip olsaydı harika olurdu. MySQL şimdi buna sahip mi? Bugünlerde React yapıyorum: P