簡體   English   中英

MySQL在另一個存儲過程中創建存儲過程

[英]MySQL Create Stored Procedure within another Stored Procedure

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的預准備語句創建存儲過程 ,並且通常無法在預准備語句做任何事情都無法在存儲過程中進行

可以將過程的create語句入字符串中(可以select routine_definition into myVar from information_schema.routines where routine_name='myRoutine' )可以用於例如使存儲過程創建一個包含要更新的命令的單個sql腳本。存儲過程。 然后,您需要在運行上述SP之后手動執行該sql(或從shell腳本,cron-job等執行),然后進行所需的更改。

編輯:所有這些都假定動態更新SP確實是您要執行的操作。 可能會有更清潔的解決方案,例如制作要修改的硬編碼部分,而不是使用輸入參數,或從表中讀取...

我對推薦此方法有些猶豫,因此請考慮以下內容作為概念驗證,而不是經過實踐檢驗的方法,但是可以在存儲過程中創建存儲例程(過程或函數)。

如果要從另一個過程內部使用標准的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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM