Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Construindo dinamicamente o código MySQL para criar um gatilho


Não tendo recebido nenhuma solução definitiva para esta questão, comecei a preparar uma opção de prova de conceito (já que o MySQL nativamente não permitiria que você executasse o código SQL que cria um gatilho, usando instruções preparadas). Por favor, sinta-se à vontade para fazer qualquer contribuição positiva.
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 USAR, chame o Procedimento:
CALL createAuditTable('name_of_table', 'history', 'pri_key_fld', 'path/to/file.sql');

Um novo banco de dados é criado usando o nome do seu banco de dados de trabalho atual, com um sufixo "_history" anexado a ele. A tabela "name_of_table" é criada neste novo banco de dados, idêntica à tabela original. O campo "pri_key_fld" (que deve ser a chave primária/única da tabela "name_of_table") é convertido em uma chave "INDEX" comum. O objetivo disso é evitar violações exclusivas durante o registro de auditoria de várias linhas no futuro.

ENTÃO Execute o arquivo criado pelo procedimento:SOURCE 'path/to/file.sql'; (ou qualquer sintaxe alternativa para executar o SQL desse arquivo)

Algumas advertências:No momento, você só pode fornecer um campo para "pri_key_fld". Idealmente, gostaríamos de fornecer um "array" contendo todos os campos exclusivos dessa tabela. Atualmente, se você tiver mais de um campo exclusivo, as violações exclusivas impedirão que você registre mais de uma linha. E isso não é legal!

Novamente, é obviamente muito desajeitado e sem desempenho passar pelo processo de criação de um arquivo no disco, apenas para ler o SQL do mesmo arquivo no próximo comando. Uma alternativa que pode ser explorada para melhorar é esta:Execute o CALL createAuditTable parte da linha de comando, pegue a saída como texto e execute o mesmo que o SQL ali mesmo na linha de comando. Eu tentei isso no Windows PowerShell; mas a saída estava cheia de strings literais "\r\n" (representando quebras de linha). Eu não tive tempo de trabalhar imediatamente na limpeza dessa corda, então está na geladeira agora!

Finalmente, ó ninjas do MySQL, por favor, sejam gentis. Eu não sou profissional, realmente. Esta é apenas uma tentativa de cultivar sua própria mercearia para resolver um problema prático.

Obrigada.