如何在存储过程备份脚本(MySQL)中使用PREPARE和EXECUTE

问题描述 投票:0回答:1

我有一个存储过程,它循环遍历模式(customerinfo)中的所有表,并通过重命名它们以包含日期并将它们复制到另一个模式(customerinfo_bak)来备份它们。

看起来如下

DELIMITER $$
CREATE PROCEDURE backup_tables()
BEGIN
  DECLARE DONE INT DEFAULT FALSE;
  DECLARE tableName VARCHAR(255);
  DECLARE cursorController CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema = 'customerinfo';
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = TRUE;
  
  OPEN cursorController;
  CREATE DATABASE IF NOT EXISTS customerinfo_bak;
  REPEAT
  
    SET @backup_table = CONCAT('customerinfo_bak.', tableName, '_backup_', DATE_FORMAT(CURDATE(), '%Y%m%d'));
    SET @sqlstmt = CONCAT('CREATE TABLE ', @backup_table, ' LIKE customerinfo.', tableName);
    PREPARE stmt FROM @sqlstmt;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    SET @sqlstmt = CONCAT('INSERT INTO ', @backup_table, ' SELECT * FROM customerinfo.', tableName);
    PREPARE stmt FROM @sqlstmt;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
  
  UNTIL DONE 
  END REPEAT;
  
  CLOSE cursorController;

END$$
DELIMITER ;

我遇到的问题是 SP 已保存并在我的架构中可见,但是在使用

 CALL customerinfo_bak.backup_tables();
调用它时,我收到以下错误:

错误代码:1064。您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,了解在第 1 行“NULL”附近使用的正确语法

我怀疑这与我如何在 PREPARE 部分中使用本地和会话变量有关,基于此 MySQL Error 1064 : close NULL at line 1 Bill Karwin 的回答,但我不明白为什么会出现这个错误将显示为 NULL 语法问题。

如果有人知道我为什么不能打电话给我的 SP,我将不胜感激。

mysql stored-procedures database-backups
1个回答
0
投票

好吧,我找到了答案(感谢 ChatGPT),事实证明这是由于一个非常简单的疏忽造成的。事实上,“tableName”变量没有在循环内填充。当'tableName'为NULL时,与NULL连接会导致NULL,从而导致SQL语法错误。

更正后的代码如下所示:

DELIMITER $$
CREATE PROCEDURE backup_tables()
BEGIN
  DECLARE DONE INT DEFAULT FALSE;
  DECLARE tableName VARCHAR(255);
  DECLARE cursorController CURSOR FOR SELECT table_name FROM information_schema.tables WHERE table_schema = 'customerinfo';
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET DONE = TRUE;
  
  OPEN cursorController;
  CREATE DATABASE IF NOT EXISTS customerinfo_bak;
  REPEAT
    FETCH cursorController INTO tableName;
    
    IF NOT DONE THEN
      SET @backup_table = CONCAT('customerinfo_bak.', tableName, '_backup_', DATE_FORMAT(CURDATE(), '%Y%m%d'));
      SET @sqlstmt = CONCAT('CREATE TABLE ', @backup_table, ' LIKE customerinfo.', tableName);
      PREPARE stmt FROM @sqlstmt;
      EXECUTE stmt;
      DEALLOCATE PREPARE stmt;

      # Prepare and execute INSERT INTO statement
      SET @sqlstmt = CONCAT('INSERT INTO ', @backup_table, ' SELECT * FROM customerinfo.', tableName);
      PREPARE stmt FROM @sqlstmt;
      EXECUTE stmt;
      DEALLOCATE PREPARE stmt;
    END IF;
  
  UNTIL DONE 
  END REPEAT;
  
  CLOSE cursorController;

END$$
DELIMITER ;

变化:

  1. 添加了 FETCH 游标控制器 INTO 表名;将游标中的表名提取到 tableName 变量中。
  2. 添加了 IF NOT DONE THEN 检查,以确保在继续循环的其余部分之前游标获取不会返回 NULL 值。

对于任何想要将此方法用作自动备份脚本的人,您可以将其创建为事件,并通过在事件中调用 SP 来安排其定期运行。

CREATE EVENT backup_event
ON SCHEDULE EVERY 1 DAY  -- Adjust schedule as needed
DO
  CALL backup_tables();
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.