在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中实现这一目标?
答案 0 :(得分:1)
您不能在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'
),例如,可用于存储您的文件procedure创建一个sql脚本,其中包含更新存储过程的命令。然后,在运行上面的SP之后,您手动(或从shell脚本,cron-job,无论如何)执行该sql,然后它将进行所需的更改。
编辑:这一切都假设动态更新SP实际上是您想要做的事情。可能有更清洁的解决方案,例如使您想要修改的硬编码部分取而代之的是输入参数,或者从表中读取...
答案 1 :(得分:1)
我对推荐这种方法有点犹豫,因此请考虑以下内容作为概念证明而不是经过试验和测试的方法,但可以在存储过程中创建存储的例程(过程或函数)。
如果要在另一个过程中使用标准CREATE PROCEDURE p_name () ...
语法,@ AC的答案是正确的。但是,过程和函数存储在mysql
。proc
表中,如果您对该表具有INSERT权限,则可以将例程的参数,正文和关联的元数据作为值直接添加到相应的列中。
如果您分解了例程,以便插入正确的列,它将起作用(下面的概念证明)。但是,虽然某些列上的错误会导致有用的消息说明特定值不合适的原因,但此方法不会捕获params_list
,body
和body_utf8
列中的语法错误当您尝试使用它时,会导致令人讨厌的运行时错误。 e.g。
错误代码: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)