sql >> Base de Datos >  >> RDS >> Mysql

Construcción dinámica de código MySQL para crear un activador

Al no haber recibido ninguna solución definitiva para esta pregunta, procedí a improvisar una opción de prueba de concepto (ya que MySQL de forma nativa no le permitiría ejecutar código SQL que crea un disparador, usando declaraciones preparadas). Siéntase libre de hacer cualquier aporte positivo.

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 ;  

PARA UTILIZAR, llame al Procedimiento:

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

Se crea una nueva base de datos utilizando el nombre de su base de datos de trabajo actual, con el sufijo "_history" adjunto. La tabla "nombre_de_tabla" se crea en esta nueva base de datos, idéntica a la tabla original. El campo "pri_key_fld" (que debería ser la clave principal/única de la tabla "nombre_de_tabla") se convierte en una clave "ÍNDICE" ordinaria. El propósito de esto es evitar infracciones únicas durante el registro de auditoría de varias filas en el futuro.

ENTONCES Ejecute el archivo creado por el procedimiento:SOURCE 'path/to/file.sql'; (o cualquier sintaxis alternativa para ejecutar SQL desde ese archivo)

Un par de advertencias:en este momento, solo puede proporcionar un campo para "pri_key_fld". Idealmente, nos gustaría proporcionar una "matriz" que contenga todos los campos únicos en esa tabla. Actualmente, si tiene más de un campo único, las infracciones únicas le impedirán registrar más de una fila. ¡Y eso no es agradable!

Nuevamente, obviamente es muy torpe y no funciona pasar por el proceso de crear un archivo en el disco, solo para leer SQL del mismo archivo en el siguiente comando. Una alternativa que se puede explorar para mejorar es esta:Ejecute CALL createAuditTable parte de la línea de comando, captura la salida como texto, luego ejecuta lo mismo que SQL allí mismo en la línea de comando. Lo intenté en Windows PowerShell; pero la salida estaba plagada de cadenas literales "\r\n" (que representan saltos de línea). No tuve tiempo de trabajar inmediatamente en la limpieza de este hilo, ¡así que ahora está en el refrigerador!

Finalmente, oh ninjas de MySQL, por favor sean amables. No soy un profesional, de verdad. Este es solo un intento de hacer crecer su propia tienda de comestibles para resolver un problema práctico.

Gracias.