错误代码:1111。无效使用存储过程内的组函数来自动创建触发器

时间:2016-03-02 01:04:07

标签: mysql stored-procedures triggers

我正在尝试创建一个存储过程,该存储过程会自动为我的数据库中存在的所有表创建触发器。 我提出了以下代码,但是当我运行它时出现了这个错误: Error Code: 1111. Invalid use of group function

DELIMITER $$                                                                                                                                                                    

DROP PROCEDURE IF EXISTS procCountAllTables $$                                                                                                                                  

CREATE PROCEDURE procCountAllTables()                                                                                                                                           

BEGIN                                                                                                                                                                           
        DECLARE table_name VARCHAR(255);                                                                                                                                        
        DECLARE end_of_tables INT DEFAULT 0; 
#        DECLARE column_name VARCHAR(255);

        DECLARE cur CURSOR FOR                                                                                                                                                  
            SELECT t.table_name                                                                                                                                                 
            FROM information_schema.tables t                                                                                                                                  
            WHERE t.table_schema = DATABASE() AND t.table_type='BASE TABLE';                                                                                                    
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET end_of_tables = 1;                                                                                                           

        OPEN cur;                                                                                                                                                               

        tables_loop: LOOP                                                                                                                                                       
            FETCH cur INTO table_name;                                                                                                                                          

            IF end_of_tables = 1 THEN                                                                                                                                           
                LEAVE tables_loop;                                                                                                                                              
            END IF;                                                                                                                                                             




 #           SET @s = CONCAT('SELECT ''', table_name, ''', COUNT(*) AS Count FROM ' , table_name);                                                                               
            SET @s = CONCAT('DROP TRIGGER IF EXISTS auditemployees_insert;
            CREATE TRIGGER audit',
            table_name,
            '_insert AFTER INSERT ON ',
            table_name,
            '
            FOR EACH ROW
            BEGIN
            INSERT INTO ',
            table_name,
            '_trigger (',
            GROUP_CONCAT(CONCAT('`', column_name, '`')
                SEPARATOR ','),
            ') SELECT ',
            GROUP_CONCAT(CONCAT('`', column_name, '`')
                SEPARATOR ','),
            ' FROM ',
            table_name,
            ' WHERE id = NEW.id;
            END$$');

            PREPARE stmt FROM @s;                                                                                                                                               
            EXECUTE stmt;                                                                                                                                                       

        END LOOP;                                                                                                                                                               

        CLOSE cur;                                                                                                                                                              
    END $$                                                                                                                                                                      

DELIMITER ;

如何纠正错误?

2 个答案:

答案 0 :(得分:2)

哟,请尝试一下,伙计:

DELIMITER $$
DROP PROCEDURE IF EXISTS procCountAllTables $$
CREATE PROCEDURE procCountAllTables()
BEGIN                                                                                                                                                                           
    DECLARE table_name VARCHAR(255);                                                                                                                                        
    DECLARE end_of_tables INT DEFAULT 0; 
--  DECLARE column_name VARCHAR(255);

    DECLARE cur CURSOR FOR                                                                                                                                                  
        SELECT t.table_name                                                                                                                                                 
        FROM information_schema.tables t                                                                                                                                  
        WHERE t.table_schema = DATABASE() AND t.table_type='BASE TABLE';                                                                                                    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET end_of_tables = 1;                                                                                                           

    OPEN cur;                                                                                                                                                               

    tables_loop: LOOP                                                                                                                                                       
        FETCH cur INTO table_name;                                                                                                                                          

        IF end_of_tables = 1 THEN                                                                                                                                           
            LEAVE tables_loop;                                                                                                                                              
        END IF;                                                                                                                                                             

--      SET @s = CONCAT('SELECT ''', table_name, ''', COUNT(*) AS Count FROM ' , table_name);                                                                               
        SET @s = CONCAT(
            'DELIMITER $$',
            'DROP TRIGGER IF EXISTS auditemployees_insert; CREATE TRIGGER audit',
            table_name,
            '_insert AFTER INSERT ON ',
            table_name,
            'FOR EACH ROW BEGIN INSERT INTO ',
            table_name,
            '_trigger (',
            "GROUP_CONCAT(CONCAT('`', column_name, '`') SEPARATOR ',')",
            ') SELECT ',
            "GROUP_CONCAT(CONCAT('`', column_name, '`') SEPARATOR ',')",
            ' FROM ',
            table_name,
            "WHERE id = NEW.id;"
            "END$$"
        );

        PREPARE stmt FROM @s;                                                                                                                                               
        EXECUTE stmt;                                                                                                                                                       
    END LOOP;
    CLOSE cur;                                                                                                                                                              
END $$                                                                                                                                                                      
DELIMITER ;  

备注:

  • 实际上并不推荐这种类型/风格的实施,特别是如果你将它置于production级别的环境中
  • 这将provide输出/预期结果,但由于sacrifice实施可以improper可维护性
  • 我喜欢通过存储过程触发的idea,但不知怎的,我against
  • 我能看到的唯一错误是正确使用'"
  • 正确的缩进有帮助,相信我
  • commented行,您知道如何处理

无论如何,欢呼声

答案 1 :(得分:0)

您错过了一个查询,然后尝试将其纳入触发器代码字符串的构造中。对于每个表,您需要

SELECT GROUP_CONCAT(CONCAT('`', column_name, '`') SEPARATOR ',') INTO @columnsList FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = [the current table name]
;

您也可以在游标查询中加入INFORMATION_SCHEMA.COLUMNStable_schematable_name),并有两个字段; table_namecolumns_list

编辑:另外,我不确定准备好的语句有多少接受多个语句;因此,您可能希望准备并执行每个触发器的初步DROP,而不是(重新)创建。

此外:

  • 我不确定您对COUNT查询的目的是什么。
  • 您可能希望从光标中过滤以_trigger结尾的表名。

编辑:这样的事情...... (未经测试,所以我可能会有拼写错误或其他类似的疏忽)

DELIMITER $$                                                                                                                                                                    

DROP PROCEDURE IF EXISTS procCountAllTables $$                                                                                                                                  

CREATE PROCEDURE procCountAllTables()

BEGIN
        DECLARE table_name VARCHAR(255);
        DECLARE trigger_name VARCHAR(255);
        DECLARE target_tablename VARCHAR(255);
        DECLARE end_of_tables INT DEFAULT 0; 
        DECLARE column_names VARCHAR(1024);
        -- Be aware of GROUP_CONCAT's configured length limitation

        DECLARE cur CURSOR FOR
            SELECT t.table_name
               , GROUP_CONCAT(CONCAT("`",c.column_name,"`")) AS column_names
            FROM information_schema.tables AS t
               INNER JOIN information_schema.columns AS c
               USING (table_schema, table_name)
            WHERE t.table_schema = DATABASE() AND t.table_type='BASE TABLE'
            GROUP BY t.table_name
            ;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET end_of_tables = 1;

        OPEN cur;

        tables_loop: LOOP
            FETCH cur INTO table_name, column_names;
            IF end_of_tables = 1 THEN
                LEAVE tables_loop;
            END IF;

            SET target_tablename := CONCAT(table_name, '_trigger');
            SET trigger_name := CONCAT('audit', table_name, '_insert');

            SET @s := CONCAT("DROP TRIGGER IF EXISTS `", trigger_name, "`;");
            PREPARE stmt FROM @s;
            EXECUTE stmt;

            SET @s := CONCAT(
               "CREATE TRIGGER `", trigger_name, "` "
               "AFTER INSERT ON `", table_name, "` "
               "FOR EACH ROW "
               "INSERT INTO `", target_tablename, "` (", column_names, ") "
               "SELECT ", column_names, " "
               "FROM `", table_name, "` "
               "WHERE id = NEW.id "
               ";"
            ;    
            PREPARE stmt FROM @s;
            EXECUTE stmt;                                                                                                                                                       

        END LOOP;                                                                                                                                                               

        CLOSE cur;                                                                                                                                                              
    END $$                                                                                                                                                                      

DELIMITER ;

请注意,我已从触发器定义中删除了BEGINEND。由于触发器本身只执行一个语句,我认为它们不是必需的;在我对这些特定类型任务的有限经验中,分隔符覆盖(实际上没有在prepare语句中完成)往往会混淆准备好的语句。

此外,如果光标改变如此,你甚至可以在触发器中没有使用选择的情况下离开:

SELECT t.table_name
, GROUP_CONCAT(CONCAT("`", column_name, "`")) AS column_names
, GROUP_CONCAT(CONCAT("NEW.`", column_name, "`")) AS source_list
...

并且触发器插件改变如下:

...
"INSERT INTO `", target_tablename, "` (", column_names, ") "
"VALUES (", source_list, ") "
";"

当然,您需要将光标中的source_list提取到source_list本地变量中。