动态 SQL 是一项能够执行某些 SQL 命令的技术,这些命令直到即将执行时才是已知的。

到目前为止,在 SPL 程序中演示的 SQL 命令都是静态 SQL:必须先知道完整的命令(变量除外)并将其编码到程序中,然后程序本身才能开始执行。因此,通过使用动态 SQL,执行的 SQL 可在程序运行时发生更改。

此外,动态 SQL 是唯一可从 SPL 程序中执行数据定义命令(如CREATE TABLE)的唯一方式。

但是,请注意,动态 SQL 的运行时性能将比静态 SQL 慢。

EXECUTE IMMEDIATE 命令用于动态运行 SQL 命令。

EXECUTE IMMEDIATE 'sql_expression;'
  [ INTO { variable [, ...] | record } ]
  [ USING expression [, ...] ]

sql_expression 是一个字符串表达式,其中包含要动态执行的 SQL 命令。variable 通常从 SELECT 命令接收结果集的输出,该命令是因执行 sql_expression 中执行 SQL 命令而创建的。变量的数字、顺序必须与结果集字段的数字、顺序相匹配,并且变量的类型必须与结果集字段的类型兼容。换言之,只要记录字段的数字、顺序与结果集的相匹配,并且记录字段的类型与结果集的类型兼容,就可以指定记录。当使用 INTO 子句时,只能在结果集中刚好返回一行,否则会发生异常。当使用 USING 子句时,expression 的值将传递给占位符。占位符通过可使用变量的 sql_expression 嵌入在 SQL 命令中。占位符可由带有冒号前缀 (:) 的标识符 (:name) 表示。求值表达式的数字、顺序必须与 sql_expression 中占位符的数字、顺序相匹配,并且求值表达式的数据类型必须与这些占位符的类型兼容。请注意,占位符不在 SPL 程序的任何位置声明 – 它们仅出现在 sql_expression 中。

以下示例显示字符串文本形式的动态 SQL 命令。

DECLARE
    v_sql           VARCHAR2(50);
BEGIN
    EXECUTE IMMEDIATE 'CREATE TABLE job (jobno NUMBER(3),' ||
        ' jname VARCHAR2(9))';
    v_sql := 'INSERT INTO job VALUES (100, ''ANALYST'')';
    EXECUTE IMMEDIATE v_sql;
    v_sql := 'INSERT INTO job VALUES (200, ''CLERK'')';
    EXECUTE IMMEDIATE v_sql;
END;

以下示例阐释用于将值传递给 SQL 字符串中的占位符的 USING 子句。

DECLARE
    v_sql           VARCHAR2(50) := 'INSERT INTO job VALUES ' ||
                        '(:p_jobno, :p_jname)';
    v_jobno         job.jobno%TYPE;
    v_jname         job.jname%TYPE;
BEGIN
    v_jobno := 300;
    v_jname := 'MANAGER';
    EXECUTE IMMEDIATE v_sql USING v_jobno, v_jname;
    v_jobno := 400;
    v_jname := 'SALESMAN';
    EXECUTE IMMEDIATE v_sql USING v_jobno, v_jname;
    v_jobno := 500;
    v_jname := 'PRESIDENT';
    EXECUTE IMMEDIATE v_sql USING v_jobno, v_jname;
END;

以下示例显示 INTO 和 USING 子句。请注意,SELECT 命令的最后一次执行将结果返回到记录而不是各变量中。

DECLARE
    v_sql           VARCHAR2(60);
    v_jobno         job.jobno%TYPE;
    v_jname         job.jname%TYPE;
    r_job           job%ROWTYPE;
BEGIN
    DBMS_OUTPUT.PUT_LINE('JOBNO    JNAME');
    DBMS_OUTPUT.PUT_LINE('-----    -------');
    v_sql := 'SELECT jobno, jname FROM job WHERE jobno = :p_jobno';
    EXECUTE IMMEDIATE v_sql INTO v_jobno, v_jname USING 100;
    DBMS_OUTPUT.PUT_LINE(v_jobno || '      ' || v_jname);
    EXECUTE IMMEDIATE v_sql INTO v_jobno, v_jname USING 200;
    DBMS_OUTPUT.PUT_LINE(v_jobno || '      ' || v_jname);
    EXECUTE IMMEDIATE v_sql INTO v_jobno, v_jname USING 300;
    DBMS_OUTPUT.PUT_LINE(v_jobno || '      ' || v_jname);
    EXECUTE IMMEDIATE v_sql INTO v_jobno, v_jname USING 400;
    DBMS_OUTPUT.PUT_LINE(v_jobno || '      ' || v_jname);
    EXECUTE IMMEDIATE v_sql INTO r_job USING 500;
    DBMS_OUTPUT.PUT_LINE(r_job.jobno || '      ' || r_job.jname);
END;

下面是上一匿名块的输出:

JOBNO    JNAME
-----    -------
100      ANALYST
200      CLERK
300      MANAGER
400      SALESMAN
500      PRESIDENT

您可以使用 BULK COLLECT 子句将 EXECUTE IMMEDIATE 语句的结果集装入一个已命名的集合中。