【问题标题】:MySQL Create Stored Procedure within another Stored ProcedureMySQL 在另一个存储过程中创建存储过程
【发布时间】:2017-06-19 22:39:36
【问题描述】:

在 MySQL 中有没有办法从另一个存储过程中创建一个存储过程?

我们正在通过使用检查表中数据库版本的临时存储过程来处理数据库更新:

DELIMITER #
DROP PROCEDURE IF EXISTS sp_temp_update_table#
CREATE PROCEDURE sp_temp_update_table()
BEGIN
    DECLARE v_db_version DECIMAL(10,6);

    SELECT CAST(`value` AS DECIMAL(10,6)) INTO v_db_version
    FROM `db_metadata` meta
    WHERE meta.`key` = 'db_version';

    IF(v_db_version < CAST('13.30' AS DECIMAL(10,6))) THEN
        -- UPDATE STATEMENTS HERE
        ALTER TABLE `foo` ADD COLUMN `bar` INT(10);

        UPDATE `etl_metadata` SET `value` = '13.30' WHERE `key` = 'db_version';
        SELECT CONCAT('13.30 Update Applied - Original db_version=', v_db_version);
    ELSE
        SELECT '13.30 Update Not Needed';
    END IF;
END#
CALL sp_temp_update_table()#
DROP PROCEDURE IF EXISTS sp_temp_update_table#
DELIMITER ;

现在我们需要从这个过程中更新一个存储过程。

有没有办法在 MySQL 中实现这一点?

【问题讨论】:

  • 从字面上看,您可能尝试在 MySQL 中使用存储过程执行的每项任务都更容易使用 Python 或 Ruby 等脚本语言进行开发。

标签: mysql stored-procedures


【解决方案1】:

我对推荐此方法有点犹豫,因此请考虑以下作为概念证明而不是久经考验的方法,但可以在存储过程中创建存储例程(过程或函数)。

如果您想在另一个过程中使用标准的CREATE PROCEDURE p_name () ... 语法,@AC 的答案是正确的。但是,过程和函数存储在 mysql.proc 表中,如果您对该表具有 INSERT 权限,则可以将例程的参数、主体和关联的元数据作为值直接添加到相应的列中。

如果您分解您的例程以便插入到正确的列中,它将起作用(下面的概念证明)。但是,虽然某些列上的错误会产生有用的消息,说明为什么特定值不合适,但 params_listbodybody_utf8 列中的语法错误不会被此方法捕获,并且会导致看起来很讨厌当您尝试使用它时出现运行时错误。例如

错误代码:1457。无法加载例程 test.from_proc。桌子 mysql.proc 丢失、损坏或包含错误数据(内部代码 -6)

幸运的是,“坏数据”位在这里很重要,它很容易通过 UPDATE 或简单地删除有问题的例程来纠正。但是,没有任何语法检查意味着您需要在部署此方法之前进行彻底的测试。

程序

DELIMITER //

DROP PROCEDURE IF EXISTS create_routine //
CREATE PROCEDURE create_routine (
  _db CHAR(64),
  _name CHAR(64),
  _type CHAR(9),
  _sql_data_access CHAR(17),
  _is_deterministic CHAR(3),
  _security_type CHAR(7),
  _param_list BLOB,
  _returns LONGBLOB,
  _body LONGBLOB,
  _comment TEXT)
MODIFIES SQL DATA
BEGIN
  SET @sql_mode = (SELECT @@SESSION.sql_mode);
  SET @character_set_client = (SELECT @@SESSION.character_set_client);
  SET @collation_connection = (SELECT @@SESSION.collation_connection);
  SET @db_collation = (SELECT @@SESSION.collation_database);

  INSERT INTO `mysql`.`proc` SET 
    `db` = _db,
    `name` = _name,
    `type` = _type,
    `specific_name` = _name,
    `sql_data_access` = _sql_data_access,
    `is_deterministic` = _is_deterministic,
    `security_type` = _security_type,
    `param_list` = _param_list,
    `returns` = _returns,
    `body` = _body,
    `definer` = CURRENT_USER(),
    `modified` = NOW(),
    `sql_mode` = @sql_mode,
    `comment` = _comment,
    `character_set_client` = @character_set_client, 
    `collation_connection` = @collation_connection,
    `db_collation` = @db_collation,
    `body_utf8` = _body;

END //    

DELIMITER ;

用法

SET @SQL = 
"BEGIN
  SELECT UPPER(_in);
END";

call create_routine(
  'test',
  'proc_test',
  'PROCEDURE',
  'CONTAINS_SQL',
  'YES',
  'INVOKER',
  'IN _in CHAR(3)',
  '',
  @SQL,
  'Procedure generated by procedure'
);

call create_routine(
  'test',
  'func_test',
  'FUNCTION',
  'CONTAINS_SQL',
  'YES',
  'INVOKER',
  '_in CHAR(3)',
  'CHAR(3)',
  'RETURN (SELECT UPPER(_in));',
  'Function generated by procedure'
);

运行例程的结果

mysql> CALL test.proc_test('yyy');
+------------+
| UPPER(_in) |
+------------+
| YYY        |
+------------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT test.func_test('aaa');
+-----------------------+
| test.func_test('aaa') |
+-----------------------+
| AAA                   |
+-----------------------+
1 row in set (0.00 sec)

【讨论】:

  • 工作,也作为一个函数,但没有工作的是:(1)在同一会话中从 create_routine 中调用新插入的函数 _name,(2)删除并重新插入 _name不同的_body(虽然mysql.proc表中的_body字段发生了变化,但_name的调用仍然使用以前的版本)。
【解决方案2】:

你不能在 MySQL 中create a stored procedure in a prepared statement,而你通常是can't do anything in a stored procedure that you can't do in a prepared statement

可以 将过程的创建语句读取为字符串 (select routine_definition into myVar from information_schema.routines where routine_name='myRoutine'),例如,您可以使用该字符串让您的存储过程创建单个 sql 脚本包含更新存储过程的命令。在运行上述 SP 之后,您将手动(或从 shell 脚本、cron-job 等)执行该 sql,然后它会进行所需的更改。

编辑:这一切都假设动态更新 SP 确实是您想要做的。可能有更简洁的解决方案,例如让您想要修改的硬编码部分取而代之的是输入参数,或者从表中读取......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-05
    • 2016-11-11
    • 2017-10-14
    • 2021-07-10
    • 2015-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多