sql >> Database >  >> RDS >> Mysql

Dynamisch construeren van MySQL-code voor het maken van een trigger

Omdat ik geen definitieve oplossing voor deze vraag heb gekregen, ben ik overgegaan tot het samenstellen van een proof of concept-optie (aangezien MySQL je native niet toestaat SQL-code uit te voeren die een trigger creëert, met behulp van Prepared Statements). Voel je vrij om positieve input te leveren.

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 ;  

OM TE GEBRUIKEN, bel de procedure:

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

Er wordt een nieuwe database gemaakt met de naam van uw huidige werkende DB, met het achtervoegsel "_history" eraan toegevoegd. De tabel "name_of_table" wordt aangemaakt in deze nieuwe DB, identiek aan de originele tabel. Het veld "pri_key_fld" (wat de primaire/unieke sleutel van de tabel "name_of_table" zou moeten zijn) wordt omgezet in een gewone "INDEX"-sleutel. Het doel hiervan is om in de toekomst unieke overtredingen te voorkomen tijdens het loggen van meerdere rijen.

DAN Voer het bestand uit dat is gemaakt met de procedure:SOURCE 'path/to/file.sql'; (of een andere syntaxis om SQL vanuit dat bestand uit te voeren)

Een paar kanttekeningen:Op dit moment kunt u slechts één veld opgeven voor "pri_key_fld". Idealiter zouden we een "array" willen leveren met alle unieke velden in die tabel. Als u momenteel meer dan één uniek veld heeft, zullen unieke schendingen voorkomen dat u meer dan één rij registreert. En dat is niet leuk!

Nogmaals, het is duidelijk erg onhandig en niet-performant om het proces van het maken van een bestand op schijf te doorlopen, om vervolgens in de volgende opdracht SQL uit hetzelfde bestand te lezen. Een alternatief dat u kunt onderzoeken om dit te verbeteren, is dit:Voer de CALL createAuditTable uit gedeelte van de opdrachtregel, vang de uitvoer als tekst en voer hetzelfde uit als SQL daar op de opdrachtregel. Ik heb dat wel geprobeerd op Windows PowerShell; maar de uitvoer was bezaaid met letterlijke "\r\n" tekenreeksen (die regeleinden vertegenwoordigen). Ik had geen tijd om meteen aan het opruimen van dit touwtje te werken, dus het staat nu in de koelkast!

Tot slot, O gij MySQL-ninja's, wees alstublieft aardig. Ik ben geen pro, echt niet. Dit is slechts een poging om zelf een kruidenier te kweken om een ​​praktisch probleem op te lossen.

Dank je.



  1. Hoe Cosh() werkt in PostgreSQL

  2. Wanneer moet ik de mysqli (Database) verbinding sluiten?

  3. Maak een favoriete lijstweergave met gedeelde voorkeuren

  4. Postgres:FOUT:plan in cache mag het resultaattype niet wijzigen