Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Dynamisches Erstellen von MySQL-Code zum Erstellen eines Triggers

Da ich keine eindeutige Lösung für diese Frage erhalten habe, habe ich eine Proof-of-Concept-Option zusammengeschustert (da MySQL Sie nativ keinen SQL-Code ausführen lassen würde, der einen Trigger erstellt, indem Sie Prepared Statements verwenden). Bitte zögern Sie nicht, positive Beiträge zu leisten.

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 ;  

ZUR VERWENDUNG rufen Sie die Prozedur auf:

CALL createAuditTable('name_of_table', 'history', 'pri_key_fld', 'path/to/file.sql');

Eine neue Datenbank wird unter Verwendung des Namens Ihrer aktuellen Arbeitsdatenbank mit dem angehängten Suffix „_history“ erstellt. Die Tabelle "name_of_table" wird in diesem neuen DB erstellt, identisch mit der ursprünglichen Tabelle. Das Feld "pri_key_fld" (das der primäre/eindeutige Schlüssel der Tabelle "name_of_table" sein sollte) wird in einen gewöhnlichen "INDEX"-Schlüssel umgewandelt. Der Zweck hiervon besteht darin, eindeutige Verstöße während der Audit-Protokollierung mehrerer Zeilen in Zukunft zu verhindern.

DANN Führen Sie die durch folgende Prozedur erstellte Datei aus:SOURCE 'path/to/file.sql'; (oder eine andere Syntax, um SQL aus dieser Datei auszuführen)

Ein paar Vorbehalte:Im Moment können Sie nur ein Feld für "pri_key_fld" angeben. Idealerweise möchten wir ein "Array" bereitstellen, das alle eindeutigen Felder in dieser Tabelle enthält. Wenn Sie derzeit mehr als ein eindeutiges Feld haben, verhindern eindeutige Verstöße, dass mehr als eine Zeile protokolliert wird. Und das ist nicht schön!

Auch hier ist es offensichtlich sehr umständlich und nicht leistungsfähig, den Prozess der Erstellung einer Datei auf der Festplatte zu durchlaufen, nur um im nächsten Befehl SQL aus derselben Datei zu lesen. Eine Alternative zur Verbesserung ist folgende:Führen Sie CALL createAuditTable aus Teil von der Befehlszeile aus, fangen Sie die Ausgabe als Text ab und führen Sie dann das Gleiche wie SQL direkt dort auf der Befehlszeile aus. Ich habe das auf Windows PowerShell versucht; aber die Ausgabe war durchsetzt mit wörtlichen "\r\n"-Strings (die Zeilenumbrüche darstellen). Ich hatte keine Zeit, diese Saite sofort zu reinigen, also liegt sie jetzt im Kühlschrank!

Abschließend, oh ihr MySQL-Ninjas, seid bitte nett. Ich bin kein Profi, wirklich. Dies ist nur ein Versuch, ein praktisches Problem zu lösen, indem Sie Ihr eigenes Lebensmittel anbauen.

Danke.