sql >> Database teknologi >  >> RDS >> Mysql

Dynamisk konstruktion af MySQL-kode til oprettelse af en trigger

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.



  1. MySQL Workbench:Kan ikke oprette forbindelse til MySQL-server på 127.0.0.1' (10061) fejl

  2. SQLite dato og tid

  3. mysql date viser resultater i dag/i går/uge

  4. Formler til at beregne geografisk nærhed