PL 提供如下两种方法执行动态 SQL:

  • 用 EXECUTE IMMEDIATE 子句

  • 用游标 OPEN FOR,FETCH,CLOSE 子句

EXECUTE IMMEDIATE 子句

如果 SQL 语句是完整的,那么直接用 EXECUTE IMMEDIATE 执行即可。按照如下方式传入或者传出参数,并且需要使用占位符:

  • 如果动态 SQL 为 SELECT 语句,而且最多返回一行记录,那么可以通过 INTO 子句指定输出参数,USING 子句指定输入参数。

  • 如果动态 SQL 为 SELECT 语句,可能返回多条记录,那么通过 BULK COLLECT INTO 指定输出参数,USING 子句指定输入参数。

  • 如果动态 SQL 为没有 RETURNING INTO 的 DML 子句,所有参数都通过 USING 子句传入。

  • 如果动态 SQL 是带有 RETURNING INTO 的 DML 子句,那么用 USING 子句指定输入参数,RETURNING INTO 指定输出参数。

如下示例为利用动态 SQL,把 ID 号为 111 的员工,名字改成 Roger,并同步员工的电话:

  1. obclient> select EMPLOYEE_ID,FIRST_NAME,PHONE_NUMBER from EMPLOYEES where EMPLOYEE_ID=111;
  2. +-------------+------------+--------------+
  3. | EMPLOYEE_ID | FIRST_NAME | PHONE_NUMBER |
  4. +-------------+------------+--------------+
  5. | 111 | Ismael | 515.124.4369 |
  6. +-------------+------------+--------------+
  7. 1 row in set (0.00 sec)
  8. obclient> DECLARE
  9. -> V_ID NUMBER := 111;
  10. -> V_NAME VARCHAR2(20) := 'Roger';
  11. -> V_PHONE varchar2(50);
  12. -> begin
  13. -> execute immediate 'update employees set first_name= :NAME where employee_id = :ID
  14. '> RETURNING PHONE_NUMBER INTO :PHONE'
  15. -> using V_NAME, V_ID, OUT V_PHONE;
  16. -> dbms_output.put_line(v_phone);
  17. -> end;
  18. -> /
  19. Query OK, 0 rows affected (0.03 sec)
  20. 515.124.4369
  21. obclient> select EMPLOYEE_ID,FIRST_NAME,PHONE_NUMBER from EMPLOYEES where EMPLOYEE_ID=111;
  22. +-------------+------------+--------------+
  23. | EMPLOYEE_ID | FIRST_NAME | PHONE_NUMBER |
  24. +-------------+------------+--------------+
  25. | 111 | Roger | 515.124.4369 |
  26. +-------------+------------+--------------+

这里利用 USING 子句传入了 3 个参数,两个输入的参数,一个输出参数。

另外可以使用游标变量打开动态 SQL, 同样可以使用占位符,打开时用 USING 子句指定变量。

  1. obclient> DECLARE
  2. -> cv SYS_REFCURSOR;
  3. -> query_2 VARCHAR2(200) :=
  4. -> 'SELECT * FROM employees
  5. '> where employee_id = :x';
  6. -> v_employees employees%ROWTYPE;
  7. -> BEGIN
  8. -> OPEN cv FOR query_2 USING 111;
  9. -> LOOP
  10. -> FETCH cv INTO v_employees;
  11. -> EXIT WHEN cv%NOTFOUND;
  12. -> DBMS_OUTPUT.PUT_LINE(v_employees.employee_id||'-'||v_employees.first_name);
  13. -> END LOOP;
  14. -> CLOSE cv;
  15. -> END;
  16. -> /
  17. Query OK, 0 rows affected (0.06 sec)
  18. 111-Ismael

OPEN FOR,FETCH,CLOSE 子句

如果动态 SQL 语句表示返回多行的 SELECT 语句,则可以使用本机动态 SQL 对其进行如下处理:

  • 使用 OPEN FOR 语句将游标变量与动态 SQL 语句关联。 在 OPEN FOR 语句的 USING 子句中,为动态 SQL 语句中的每个占位符指定一个绑定变量。

  • 使用 FETCH 语句一次检索一个行结果,一次检索多个结果集,或一次检索全部结果集。

  • 使用 CLOSE 语句关闭游标变量。

如果集合符合“查询集合”中的条件,则动态 SQL 语句可以查询集合。

如下示例为在所有员工信息中检索经理级别的员工,并一次检索了行结果。

  1. DECLARE
  2. TYPE EmpCurTyp IS REF CURSOR;
  3. v_emp_cursor EmpCurTyp;
  4. emp_record employees%ROWTYPE;
  5. v_stmt_str VARCHAR2(200);
  6. v_e_job employees.job%TYPE;
  7. BEGIN
  8. -- Dynamic SQL statement with placeholder:
  9. v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
  10. -- Open cursor & specify bind variable in USING clause:
  11. OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
  12. -- Fetch rows from result set one at a time:
  13. LOOP
  14. FETCH v_emp_cursor INTO emp_record;
  15. EXIT WHEN v_emp_cursor%NOTFOUND;
  16. END LOOP;
  17. -- Close cursor:
  18. CLOSE v_emp_cursor;
  19. END;
  20. /