Mysql 在存储过程中使用动态SQL的解决方法是什么

Mysql 在存储过程中使用动态SQL的解决方法是什么,mysql,sql,database,stored-procedures,triggers,Mysql,Sql,Database,Stored Procedures,Triggers,存储过程 DELIMITER $$ CREATE PROCEDURE `lms`.`leads_to_bak` () BEGIN SET @table1 = (SELECT `tabler_name` FROM `sets` WHERE `on_off`=0 LIMIT 1); SET @table2 = CONCAT(@table1, '_bak'); SET @SQL1 = CONCAT('INSERT INTO ',@table2, '(', (SELECT REPLACE(GROUP_

存储过程

DELIMITER $$

CREATE PROCEDURE `lms`.`leads_to_bak` ()
BEGIN
SET @table1 = (SELECT `tabler_name` FROM `sets` WHERE `on_off`=0 LIMIT 1);
SET @table2 = CONCAT(@table1, '_bak');
SET @SQL1 = CONCAT('INSERT INTO ',@table2, '(', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @table2), ')', ' SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @table1), ' FROM ', @table1);
PREPARE stmt FROM @sql1;
EXECUTE stmt;
END$$

DELIMITER ;
触发器

DELIMITER $$
USE `lms`$$

CREATE TRIGGER `lms`.`after_insert_into_leads`
AFTER INSERT ON `sets` FOR EACH ROW
BEGIN
CALL lms.leads_to_bak();
END$$

DELIMITER ;
问题

我得到一个
错误代码:1336。在执行
插入时,存储函数或触发器
错误消息中不允许使用动态SQL,这意味着将执行触发器和存储过程。我假设这里的问题是动态SQL:

PREPARE stmt FROM @sql1;
EXECUTE stmt;

我环顾四周,发现有一个关于这个问题的问题,但没有答案。有人对合理的解决方法有什么建议吗?

在过程定义中,您需要存储所有
输入/输出变量

更改:

CREATE PROCEDURE `lms`.`leads_to_bak` ()
致:

然后称之为这样做:

CALL `lms`.`leads_to_bak`('table1', 'table2')
用自己的字符串替换字符串


使用存储过程的目的是防止使用严格类型的数据进行SQL注入。如果只在参数列表中发送严格类型的输入变量,则从技术上讲不需要在存储过程中准备它

这样,您可以在调用存储过程之前处理字符串操作。保持你存储的进程精简

下面是我的一个存储过程的示例:

DELIMITER ;
DROP PROCEDURE IF EXISTS `save_player`;
DELIMITER //

CREATE PROCEDURE `save_player` (
IN uid int(15) UNSIGNED,
IN email varchar(100),
IN name varchar(100),
IN passwd char(96),
IN state ENUM('active','suspended','deleted'),
IN user_role ENUM('gamemaster','moderator','player'),
IN locale ENUM('en','fr'),
IN lvl tinyint(1),
IN hp bigint(20),
IN reborn tinyint(1),
IN cross_ref varchar(12),
IN email_verified tinyint(1),
OUT new_id  int(15) UNSIGNED
)
BEGIN
   DECLARE date_deleted timestamp DEFAULT NULL;
   IF uid > 0 AND EXISTS (SELECT id FROM user WHERE `id`= uid) THEN
      IF state = 'deleted' THEN
        SET date_deleted = CURRENT_TIMESTAMP;
      END IF ;
      UPDATE `user` SET
        `email` = email,
        `name` = name,
        `passwd` = passwd,
        `state` = state,
        `user_role` = user_role,
        `locale` = locale,
        `lvl` = lvl,
        `hp` = hp,
        `reborn` = reborn,
        `cross_ref` = cross_ref,
        `email_verified` = email_verified,
        `date_deleted` = date_deleted
      WHERE `id` = uid;
      SET new_id = uid;
   ELSE
      INSERT INTO user (`email`, `name`, `passwd`, `state`, `user_role`, `locale`, `lvl`, `hp`, `reborn`, `cross_ref`, `email_verified`, `date_created`)
             VALUES (email, name, passwd, state, user_role, locale, lvl, hp, reborn, cross_ref, email_verified, NOW());
      SELECT LAST_INSERT_ID()  INTO new_id;
   END IF;
 END //
DELIMITER ;

对于MySQL函数中缺少动态SQL没有很好的解决方法,只是一些笨拙的工作。有些事情仍然完全无法掌握,例如在SQL查询中使用动态计算的字段名或表名。是的,偶尔需要做这种事情

不要试图通过将动态SQL放在存储过程中并包装在函数或触发器中进行欺骗,因为poser尝试过的问题-MySQL太聪明了,会给你通常不明显的错误消息。相信我,我去过所有的房子


来自Oracle PL/SQL和MS SQL Server的背景,我非常怀念PL/SQL和(在某种程度上)T-SQL为编写过程SQL提供的丰富内容

谢谢,但是在不使用
PREPARE
的情况下,如何在存储过程中执行字符串操作呢?这就是导致错误的原因>使用存储过程的目的是防止使用严格类型的数据进行SQL注入。如果只在参数列表中发送严格类型的输入变量,从技术上讲不需要在存储过程中准备它,这非常有用。谢谢因此,从本质上讲,使用输入变量参数创建过程,然后在过程中设置它们,而不使用
PREPARE
,也会做同样的事情,但不会引发动态sql错误。我将附上一个我一直在使用的存储过程的示例。让我看看我是否理解您给出的示例。你是说像这样的事吗?如果有人对某个帖子投了反对票,他们至少可以体面地解释为什么他们不赞成。出于充分的理由,反对票和反对票是匿名的。我们到处都会得到一些随机的反对票。我理解这一点,但如果我做错了什么,我想知道我将来可以改进什么。是的,很多用户都喜欢这样。还有一些人表现出报复行为(正因为如此,许多人更喜欢沉默的向下投票)。或者他们只是懒得解释原因。可能是重复的
DELIMITER ;
DROP PROCEDURE IF EXISTS `save_player`;
DELIMITER //

CREATE PROCEDURE `save_player` (
IN uid int(15) UNSIGNED,
IN email varchar(100),
IN name varchar(100),
IN passwd char(96),
IN state ENUM('active','suspended','deleted'),
IN user_role ENUM('gamemaster','moderator','player'),
IN locale ENUM('en','fr'),
IN lvl tinyint(1),
IN hp bigint(20),
IN reborn tinyint(1),
IN cross_ref varchar(12),
IN email_verified tinyint(1),
OUT new_id  int(15) UNSIGNED
)
BEGIN
   DECLARE date_deleted timestamp DEFAULT NULL;
   IF uid > 0 AND EXISTS (SELECT id FROM user WHERE `id`= uid) THEN
      IF state = 'deleted' THEN
        SET date_deleted = CURRENT_TIMESTAMP;
      END IF ;
      UPDATE `user` SET
        `email` = email,
        `name` = name,
        `passwd` = passwd,
        `state` = state,
        `user_role` = user_role,
        `locale` = locale,
        `lvl` = lvl,
        `hp` = hp,
        `reborn` = reborn,
        `cross_ref` = cross_ref,
        `email_verified` = email_verified,
        `date_deleted` = date_deleted
      WHERE `id` = uid;
      SET new_id = uid;
   ELSE
      INSERT INTO user (`email`, `name`, `passwd`, `state`, `user_role`, `locale`, `lvl`, `hp`, `reborn`, `cross_ref`, `email_verified`, `date_created`)
             VALUES (email, name, passwd, state, user_role, locale, lvl, hp, reborn, cross_ref, email_verified, NOW());
      SELECT LAST_INSERT_ID()  INTO new_id;
   END IF;
 END //
DELIMITER ;