Uden at have modtaget nogen konkret løsning på dette spørgsmål, er jeg gået videre med at samle en proof of concept-mulighed (da MySQL oprindeligt ikke ville lade dig køre SQL-kode, der opretter en trigger, ved hjælp af Prepared Statements). Du er velkommen til at komme med positive input.
DELIMITER //
DROP PROCEDURE IF EXISTS createAuditTable//
CREATE PROCEDURE createAuditTable(tblname CHAR(30), sufftxt CHAR(10), pri CHAR(20), filename CHAR(255) )
BEGIN
SELECT DATABASE() INTO @dbname;
SET @srctbl = CONCAT(@dbname, ".", tblname);
SET @destdb = CONCAT(@dbname, "_", sufftxt);
SET @desttbl = CONCAT(@destdb, ".", tblname);
SET @str1 = CONCAT( "CREATE DATABASE IF NOT EXISTS ", @destdb);
PREPARE stmt1 FROM @str1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
SET @str2 = "SET FOREIGN_KEY_CHECKS=0";
PREPARE stmt2 FROM @str2;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
SELECT COUNT(*) FROM information_schema.tables WHERE table_name = tblname AND table_schema = @destdb INTO @tblcount;
IF (@tblcount = 0) THEN
SET @str3 = CONCAT("CREATE TABLE ", @desttbl, " LIKE ", @srctbl);
PREPARE stmt3 FROM @str3;
EXECUTE stmt3;
DEALLOCATE PREPARE stmt3;
END IF;
SELECT COUNT(*) FROM information_schema.columns WHERE table_name = tblname AND table_schema = @destdb AND column_key = 'PRI' INTO @keycount;
IF (@keycount <> 0) THEN
SET @str4 = CONCAT("ALTER TABLE ", @desttbl, " DROP PRIMARY KEY, ADD INDEX ", pri, " (", pri, ")" );
PREPARE stmt4 FROM @str4;
EXECUTE stmt4;
DEALLOCATE PREPARE stmt4;
END IF;
SELECT CONCAT( "DELIMITER $$
DROP TRIGGER IF EXISTS ", tblname, "_history_BU$$
CREATE TRIGGER ", tblname, "_history_BU
BEFORE UPDATE ON ", tblname, "
FOR EACH ROW
BEGIN
INSERT INTO ", @desttbl, " (",
(SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_schema = @dbname AND table_name = tblname), ") ",
"
VALUES(",
(SELECT GROUP_CONCAT('OLD.', column_name) FROM information_schema.columns WHERE table_schema = @dbname AND table_name = tblname),
");
END$$
DELIMITER ;"
) AS qstr FROM DUAL INTO @triggertxt;
SET @savestr = CONCAT('SELECT ', '"', @triggertxt, '"', " INTO DUMPFILE ", '"', filename, '"');
PREPARE stmt5 FROM @savestr;
EXECUTE stmt5;
DEALLOCATE PREPARE stmt5;
END//
DELIMITER ;
FOR AT BRUGE, ring til Proceduren:
CALL createAuditTable('name_of_table', 'history', 'pri_key_fld', 'path/to/file.sql');
En ny database oprettes ved at bruge navnet på din nuværende arbejdsdatabase, med et suffiks af "_historie" tilføjet til det. Tabellen "navn_på_tabel" er oprettet i denne nye DB, identisk med den originale tabel. Feltet "pri_key_fld" (som skal være den primære/unikke nøgle i tabellen "navn_på_tabel") konverteres til en almindelig "INDEX" nøgle. Formålet med dette er at undgå unikke overtrædelser under revisionslogning af flere rækker i fremtiden.
SÅ Kør filen oprettet ved proceduren:SOURCE 'path/to/file.sql';
(eller enhver alternativ syntaks til at køre SQL fra den fil)
Et par advarsler:Lige nu kan du kun angive ét felt til "pri_key_fld". Ideelt set ønsker vi at levere et "array", der indeholder alle de unikke felter i den tabel. I øjeblikket, hvis du har mere end ét unikt felt, vil unikke overtrædelser forhindre dig i at logge mere end én række. Og det er ikke rart!
Igen er det åbenlyst meget klodset og ikke-performant at gennemgå processen med at oprette en fil på disk, for så at læse SQL fra den samme fil i den næste kommando. Et alternativ, man kan udforske for at forbedre, er dette:Kør CALL createAuditTable
del fra kommandolinjen, fange output som tekst, og kør derefter det samme som SQL lige der på kommandolinjen. Jeg forsøgte det på Windows PowerShell; men outputtet var fyldt med bogstavelige "\r\n" strenge (der repræsenterer linjeskift). Jeg havde ikke tid til straks at arbejde på at rense denne snor, så den er i køleskabet nu!
Til sidst, O I MySQL ninjaer, vær søde. Jeg er ikke pro, virkelig. Dette er blot et forsøg på at dyrke-din-egen-købmand på at løse et praktisk problem.
Tak.