Oracle兼容-语法-EXECUTE IMMEDIATE


1. 语法

  1. EXECUTE IMMEDIATE dynamic_sql_stmt
  2. [ { into_clause | bulk_collect_into_clause } [ using_clause ]
  3. | using_clause
  4. ] ;
  1. using_clause ::=
  2. USING [ IN | OUT | IN OUT ] bind_argument
  3. [ [,] [ [ IN | OUT | IN OUT ] bind_argument ]...
  1. into_clause ::=
  2. INTO { variable [, variable ]... | record )

sql_mode 切换到 ORACLE 模式下时,在存储过程中支持如下用法:

  1. bulk_collect_into_clause ::=
  2. BULK COLLECT INTO { collection | :host_array }
  3. [, { collection | :host_array } ]...

2. 定义和用法

EXECUTE IMMEDIATE 用于动态执行SQL语句,是指先把一个SQL命令保存到一个字符串中,然后通过 EXECUTE IMMEDIATE 命令动态执行字符串中的SQL语句,以实现SQL语句的动态生成。

EXECUTE IMMEDIATE 使用时有以下几点注意事项:

    1. 参数 USING 支持使用表达式作为预处理变量。
    1. 参数 USING OUT / USING IN OUT 支持对变量进行赋值。
    1. 支持使用 INTO 将返回结果赋给变量。
    1. DYNAMIC_SQL_STMT 子句中支持使用变量。
    1. DYNAMIC_SQL_STMT 子句中不支持含有 INTO 的语法。

在GreatSQL和Oracle中的差异点及其他注意事项:

  • DYNAMIC_SQL_STMT 子句不支持匿名存储过程(例如:BEGIN SELECT :1, :2; END;)运行。

  • 在Oracle中,作为 USING OUT 时在语法上允许 USING OUT TO_CHAR(...) 这种语句运行,但会导致Oracle单个进程断开连接,因此如果用到需要 USING OUT 支持存储过程赋值的情况,将会限制只能使用变量或者系统变量作为参数。

  1. > EXECUTE IMMEDIATE 'CALL p1(?)' USING OUT var; <--允许
  2. > EXECUTE IMMEDIATE 'CALL p1(?)' USING OUT @var; <--允许
  3. > EXECUTE IMMEDIATE 'CALL p1(?)' USING OUT TO_CAHR(@var); <-- 被禁止
  • 语句 USING OUT 将被视为 USING IN OUT
  1. greatsql> DROP PROCEDURE IF EXISTS p1;
  2. greatsql> DELIMITER //
  3. greatsql> CREATE PROCEDURE IF NOT EXISTS p1(i INT)
  4. AS
  5. BEGIN
  6. i:= 10;
  7. END;
  8. EXECUTE IMMEDIATE 'CALL p1(?)' USING OUT a;

在GreatSQL中允许这种方式运行,而在Oracle中会报错,要求 USING IN OUT var 或者 USING IN var;

  1. > EXECUTE IMMEDIATE 'SELECT ?' USING OUT a;
  2. ERROR HY000: Incorrect arguments to using OUT param var only use for call

上述SQL命令在GreatSQL运行时将会引发报错。

  • 同时使用 USING IN OUTINTO 属于未定义行为,OUT 不一定能够正确赋值。

  • 在使用 USING 字段的时候,预处理的数据内容需要根据数据类型进行判断的时候,默认将会使用字符串类型,部分函数需要类型转换后才可以使用。

  1. greatsql> DELIMITER //
  2. greatsql> DECLARE
  3. v DATETIME;
  4. BEGIN
  5. v:= TO_DATE('22:31:23', 'HH24:MI:SS');
  6. EXECUTE IMMEDIAtE 'SELECT TO_ChAR(?, ''HH24:MI'')' USING v;
  7. END; //
  8. ERROR 3064 (HY000): Incorrect type for argument args 0 in function to_char.
  9. greatsql> DELIMITER //
  10. greatsql> DECLARE
  11. v DATETIME;
  12. BEGIN
  13. v:= TO_DATE('22:31:23', 'hh24:mi:ss');
  14. EXECUTE IMMEDIATE 'SELECT TO_CHAR(CAST( ? AS DATETIME) , ''hh24:mi'')' USING v;
  15. END; //
  16. +-------------------------------------------+
  17. | TO_CHAR(CAST( ? AS DATETIME) , 'hh24:mi') |
  18. +-------------------------------------------+
  19. | 22:31 |
  20. +-------------------------------------------+

3. 示例

先创建基本测试表t1:

  1. greatsql> DROP TABLE IF EXISTS t1;
  2. greatsql> CREATE TABLE IF NOT EXISTS t1
  3. (
  4. id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  5. a INT,
  6. PRIMARY KEY(id));
  • 支持DDLDMLTCL(事务控制语言)语句。
  1. EXECUTE IMMEDIATE 'CREATE TABLE t1 (a INT DEFAULT 10, b varchar(255) NOT NULL DEFAULT 1);';
  2. EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS t1;';
  3. EXECUTE IMMEDIATE 'ALTER TABLE \`t1\` ADD COLUMN \`b\` varchar(255) NOT NULL DEFAULT 1;';
  4. EXECUTE IMMEDIATE 'CREATE VIEW v1 AS SELECT * FROM t1;';
  5. EXECUTE IMMEDIATE 'DROP VIEW IF EXISTS v1;';
  6. EXECUTE IMMEDIATE 'ALTER TABLE t1 ADD INDEX k1 (a) ;';
  7. EXECUTE IMMEDIATE 'DROP INDEX k1 ON t1;';
  8. EXECUTE IMMEDIATE 'INSERT INTO t1 VALUES (2,3)';
  9. EXECUTE IMMEDIATE 'UPDATE t1 SET a=3 WHERE a=2;';
  10. EXECUTE IMMEDIATE 'DELETE FROM t1 WHERE a=2;';
  11. EXECUTE IMMEDIATE 'SELECT * FROM t1;';
  12. EXECUTE IMMEDIATE 'ROLLBACK';
  13. EXECUTE IMMEDIATE 'COMMIT;';
  • 支持 SET 赋值临时变量。
  1. EXECUTE IMMEDIATE 'SET @param=(SELECT a FROM t1 LIMIT 1);';
  2. SELECt @param;
  • 支持利用 USING 绑定参数与表达式。
  1. greatsql> SET @a=1;
  2. greatsql> EXECUTE IMMEDIATE 'INSERT INTO t1 VALUES (?,1)' USING @a;
  3. Query OK, 1 row affected (0.00 sec)
  4. greatsql> EXECUTE IMMEDIATE 'SELECT ?' USING TO_CHAR(1);
  5. +------+
  6. | ? |
  7. +------+
  8. | 1 |
  9. +------+
  10. -- 切换sql_modeORACLE
  11. greatsql> SET sql_mode = ORACLE;
  12. greatsql> DELIMITER //
  13. greatsql> DECLARE
  14. v1 INT:= 2;
  15. BEGIN
  16. EXECUTE IMMEDIATE 'SELECT ?' USING v1;
  17. END; //
  18. +------+
  19. | ? |
  20. +------+
  21. | 2 |
  22. +------+
  • 支持存储过程中使用 EXECUTE IMMEDIATE。
  1. greatsql> DROP PROCEDURE IF EXISTS p1;
  2. greatsql> DELIMITER //
  3. CREATE PROCEDURE IF NOT EXISTS p1 (x int)
  4. BEGIN
  5. SET @a = x;
  6. EXECUTE IMMEDIATE 'SELECT * FROM t1 WHERE a = ?;' USING @a;
  7. EXECUTE IMMEDIATE 'ALTER TABLE \`t1\` add column \`c\` VARCHAR(255) NOT NULL DEFAULT 123456;';
  8. EXECUTE IMMEDIATE 'SELECT * FROM t1 WHERE a = ?;' USING @a;
  9. END; //
  10. greatsql> CALL p1(1); //
  11. +----+------+
  12. | id | a |
  13. +----+------+
  14. | 1 | 1 |
  15. +----+------+
  16. 1 row in set (0.00 sec)
  17. +----+------+--------+
  18. | id | a | c |
  19. +----+------+--------+
  20. | 1 | 1 | 123456 |
  21. +----+------+--------+
  • 调用存储过程。
  1. greatsql> DROP PROCEDURE IF EXISTS p2;
  2. greatsql> DELIMITER //
  3. greatsql> CREATE PROCEDURE IF NOT EXISTS p2(OUT a INT)
  4. BEGIN
  5. SET a:= 10;
  6. SELECT CONCAT("IN a=", a);
  7. END; //
  8. greatsql> DELIMITER ;
  9. greatsql> SET @a = 2;
  10. greatsql> EXECUTE IMMEDIATE 'CALL p2(?)' USING @a;
  11. +--------------------+
  12. | CONCAT("IN a=", a) |
  13. +--------------------+
  14. | IN a=10 |
  15. +--------------------+
  16. greatsql> SELECT @a;
  17. +------+
  18. | @a |
  19. +------+
  20. | 2 |
  21. +------+
  22. -- 注意和上例多了个 OUT 关键字后,返回结果不同
  23. greatsql> EXECUTE IMMEDIATE 'CALL p2(?)' USING OUT @a;
  24. +--------------------+
  25. | CONCAT("IN a=", a) |
  26. +--------------------+
  27. | IN a=10 |
  28. +--------------------+
  29. greatsql> SELECT @a;
  30. +------+
  31. | @a |
  32. +------+
  33. | 10 |
  34. +------+
  35. greatsql> DROP PROCEDURE IF EXISTS p2;
  • 变量作为语句。
  1. greatsql> SET @v = 'SELECT "GreatSQL"';
  2. greatsql> EXECUTE IMMEDIATE @v;
  3. +----------+
  4. | GreatSQL |
  5. +----------+
  6. | GreatSQL |
  7. +----------+
  • 支持存储过程中的变量作为语句。
  1. -- 切换sql_modeORACLE
  2. greatsql> SET sql_mode = ORACLE;
  3. greatsql> DELIMITER //
  4. greatsql> DECLARE
  5. v VARCHAR(200);
  6. BEGIN
  7. v:= 'SELECT \'GreatSQL\', ' || TO_CHAR(3306);
  8. EXECUTE IMMEDIATE v;
  9. END; //
  10. +----------+------+
  11. | GreatSQL | 3306 |
  12. +----------+------+
  13. | GreatSQL | 3306 |
  14. +----------+------+
  • 支持用 INTO/BULK COLLECT INTO 获取查询结果。
  1. greatsql> EXECUTE IMMEDIATE 'SELECT \'GreatSQL\'' INTO @v;
  2. greatsql> SELECT @v;
  3. +----------+
  4. | @v |
  5. +----------+
  6. | GreatSQL |
  7. +----------+
  8. greatsql> SET sql_mode = ORACLE;
  9. greatsql> DELIMITER //
  10. greatsql> DECLARE
  11. v1 INT := 2;
  12. BEGIN
  13. EXECUTE IMMEDIATE 'SELECT ?' INTO v1 USING 1;
  14. SELECT v1;
  15. END; //
  16. +------+
  17. | v1 |
  18. +------+
  19. | 1 |
  20. +------+
  21. greatsql> SET sql_mode = ORACLE;
  22. greatsql> DELIMITER //
  23. greatsql> DECLARE
  24. TYPE 1type IS RECORD
  25. (
  26. a INT
  27. );
  28. TYPE array_param_type IS TABLE OF 1type;
  29. ary array_param_type;
  30. BEGIN
  31. EXECUTE IMMEDIATE 'SELECT a FROM t1' BULK COLLECT INTO ary;
  32. SELECT ary;
  33. END; //
  34. +----------------------------+
  35. | ary |
  36. +----------------------------+
  37. | ARRAY_PARAM_TYPE(1type(1)) |
  38. +----------------------------+
  • 不支持使用非定义参数。
  1. greatsql> CREATE TABLE t3 (a INT DEFAULT 10);
  2. greatsql> EXECUTE IMMEDIATE 'INSERT INTO t3 VALUES (?)' USING DEFAULT;
  3. ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
  • 不支持数据库切换语句。
  1. greatsql> EXECUTE IMMEDIATE 'USE greatsql;';
  2. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  • 不支持创建/删除存储过程。
  1. greatsql> EXECUTE IMMEDIATE 'CREATE PROCEDURE p5(x int) BEGIN SET @a = x; END;';
  2. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  3. greatsql> EXECUTE IMMEDIATE 'drop PROCEDURE p1;';
  4. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  • 不支持创建/删除函数。
  1. greatsql> EXECUTE IMMEDIATE 'CREATE FUNCTION f1(a int) RETURNS INT BEGIN RETURN a * 10; END;';
  2. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  3. greatsql> EXECUTE IMMEDIATE 'DROP FUNCTION f1;';
  4. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  • 不支持创建/删除触发器。
  1. greatsql> EXECUTE IMMEDIATE 'CREATE TRIGGER trig1 BEFORE INSERT ON t1 FOR EACH ROW SET @sum = @sum + NEW.c0;';
  2. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  3. greatsql> EXECUTE IMMEDIATE 'drop TRIGGER IF EXISTS trig1;';
  4. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  • 不支持handler使用。
  1. greatsql> EXECUTE IMMEDIATE 'handler test_number open;';
  2. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  • 不支持开始事务操作。
  1. greatsql> EXECUTE IMMEDIATE 'START TRANSACTION;';
  2. ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
  • 不支持语句中带有INTO。
  1. greatsql> EXECUTE IMMEDIATE 'SELECT 1 INTO @v;';
  2. ERROR HY000: Misplaced INTO clause, INTO is not allowed inside EXECUTE IMMEDIATE query string

问题反馈

联系我们

扫码关注微信公众号

greatsql-wx