我正在尝试创建一个存储过程,该存储过程会自动为我的数据库中存在的所有表创建触发器。
我提出了以下代码,但是当我运行它时出现了这个错误:
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 ;
如何纠正错误?
答案 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.COLUMNS
(table_schema
和table_name
),并有两个字段; table_name
和columns_list
。
编辑:另外,我不确定准备好的语句有多少接受多个语句;因此,您可能希望准备并执行每个触发器的初步DROP
,而不是(重新)创建。
此外:
_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 ;
请注意,我已从触发器定义中删除了BEGIN
和END
。由于触发器本身只执行一个语句,我认为它们不是必需的;在我对这些特定类型任务的有限经验中,分隔符覆盖(实际上没有在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本地变量中。