一、SQL的执行流程
数据库中的所有SQL语句都经历了不同的阶段:•解析:预执行“这可能吗?”检查包括语法,对象存在,权限等
•绑定:获取语句中引用的任何变量的实际值
•执行:语句被执行。
•提取:结果返回给用户。
某些阶段可能与所有语句无关;例如,提取阶段适用于查询,但不适用于DML。
二、PL / SQL子程序中SQL的执行流程
当PL / SQL子程序中包含SQL语句时,解析和绑定阶段通常是在编译时完成的,也就是说,当过程,函数或包体是CREATEd时。
如果在创建过程时SQL语句的文本未知,该怎么办? Oracle服务器如何解析它? 它不能。 例如,假设您想要删除一个表,但用户在执行时输入表名:
CREATE PROCEDURE drop_any_table(p_table_name VARCHAR2)
IS BEGINDROP TABLE p_table_name; -- cannot be parsed
END;
三、动态SQL
您使用动态SQL来创建一个SQL语句,其中的文本事先不完全知道。动态SQL:
•构造并存储为子程序内的字符串。
•是否包含具有不同列数据的SQL语句,或带有或不带有占位符(绑定变量)的不同条件。
•使数据定义,数据控制或会话控制语句能够从PL / SQL写入和执行。
(1)本地动态SQL
PL / SQL不支持直接写在procedure中的DDL语句。 本地动态SQL允许您通过在子程序中构建SQL并将其存储为字符串来解决此问题。 本地动态SQL:•以PL / SQL语言直接为动态SQL提供本机支持。
•使数据定义,数据控制或会话控制语句能够从PL / SQL写入和执行。
•使用本机动态SQL语句(EXECUTE IMMEDIATE)或DBMS_SQL包执行。
•提供执行直到执行时间结构未知的SQL语句的能力。
•也可以使用OPEN-FOR,FETCH和CLOSE PL / SQL语句。
(2)使用EXECUTE IMMEDIATE语句
在PL / SQL匿名块或子程序中为原生动态SQL使用EXECUTE IMMEDIATE语句:
EXECUTE IMMEDIATE dynamic_string
[INTO {define_variable[, define_variable] ... | record}]
[USING [IN|OUT|IN OUT] bind_argument[, [IN|OUT|IN OUT] bind_argument] ... ];
•INTO用于单行查询,并指定检索列值的变量或记录。
•USING保存所有绑定参数。 如果未指定,则默认参数模式为IN。
•dynamic_string是包含SQL语句文本的字符变量或文字。
•define_variable是一个PL / SQL变量,用于存储选定的列值。
•record是存储选定行的用户定义或%ROWTYPE记录。
•bind_argument是一个表达式,其值在执行时传递给动态SQL语句。
•USING子句保存所有绑定参数。 默认参数模式是IN。
示例1:使用DDL语句的动态SQL
在线构建动态声明:
CREATE PROCEDURE drop_any_table(p_table_name VARCHAR2) IS
BEGINEXECUTE IMMEDIATE 'DROP TABLE '||p_table_name;
END;
在一个变量中构造动态语句:
CREATE PROCEDURE drop_any_table(p_table_name VARCHAR2) ISv_dynamic_stmt VARCHAR2(50);
BEGINv_dynamic_stmt := 'DROP TABLE '||p_table_name;EXECUTE IMMEDIATE v_dynamic_stmt;
END;
BEGINdrop_any_table('EMPLOYEE_NAMES');
END;
示例2:使用DML语句的动态SQL
删除任何表中的所有行并返回计数:
CREATE FUNCTION del_rows(p_table_name VARCHAR2)
RETURN NUMBER IS
BEGINEXECUTE IMMEDIATE 'DELETE FROM '||p_table_name;RETURN SQL%ROWCOUNT;
END;
调用该函数:
DECLAREv_count NUMBER;
BEGINv_count := del_rows('EMPLOYEE_NAMES');DBMS_OUTPUT.PUT_LINE(v_count|| ' rows deleted.');
END;
示例3:使用DML语句的动态SQL
这是一个将行插入两列并调用过程的示例。
CREATE PROCEDURE add_row(p_table_name VARCHAR2,p_id NUMBER, p_name VARCHAR2) IS
BEGINEXECUTE IMMEDIATE 'INSERT INTO '||p_table_name||' VALUES (p_id, p_name)';
END;
BEGIN
add_row('EMPLOYEE_NAMES', 250, 'Chang');
END;
示例4:使用本机动态SQL重新编译PL / SQL代码
您可以使用下列ALTER语句重新编译PL / SQL对象而不重新创建它们:
ALTER PROCEDURE procedure-name COMPILE;
ALTER FUNCTION function-name COMPILE;
ALTER PACKAGE package_name COMPILE SPECIFICATION;
ALTER PACKAGE package-name COMPILE BODY;
本示例创建一个过程,用于重新编译在运行时输入其名称和类型的PL / SQL对象。
CREATE PROCEDURE compile_plsql
(p_name VARCHAR2,p_type VARCHAR2,p_options VARCHAR2 := NULL) ISv_stmt VARCHAR2(200);
BEGINv_stmt := 'ALTER '||p_type||' '||p_name||' COMPILE'||' '||p_options;EXECUTE IMMEDIATE v_stmt;
END;
BEGIN compile_plsql('MYPACK','PACKAGE','BODY'); END;
四、使用DBMS_SQL包
(1)DBMS_SQL包的一些过程和功能是:• OPEN_CURSOR
• PARSE
• BIND_VARIABLE
• EXECUTE
• FETCH_ROWS
• CLOSE_CURSOR
(2)使用带有DML语句的DBMS_SQL
删除行的示例:
CREATE OR REPLACE FUNCTION del_rows
(p_table_name VARCHAR2) RETURN NUMBER ISv_csr_id INTEGER;v_rows_del NUMBER;
BEGINv_csr_id := DBMS_SQL.OPEN_CURSOR;DBMS_SQL.PARSE(v_csr_id,'DELETE FROM '||p_table_name, DBMS_SQL.NATIVE);v_rows_del := DBMS_SQL.EXECUTE (v_csr_id);DBMS_SQL.CLOSE_CURSOR(v_csr_id);RETURN v_rows_del;
END;
请将本文前面的内容与del_rows函数进行比较。 它们功能相同,但更简单?
(3)使用带有参数化DML语句的DBMS_SQL
再次,将其与本课前面的add_row过程进行比较。 你宁愿写什么?CREATE PROCEDURE add_row (p_table_name VARCHAR2,
p_id NUMBER, p_name VARCHAR2) ISv_csr_id INTEGER;v_stmt VARCHAR2(200);v_rows_added NUMBER;
BEGINv_stmt := 'INSERT INTO '||p_table_name||' VALUES ('||p_id||','''||p_name||''')';v_csr_id := DBMS_SQL.OPEN_CURSOR;DBMS_SQL.PARSE(v_csr_id,v_stmt, DBMS_SQL.NATIVE);v_rows_added := DBMS_SQL.EXECUTE(v_csr_id);DBMS_SQL.CLOSE_CURSOR(v_csr_id);
END;
五、本地动态SQL与DBMS_SQL包的比较
原生动态SQL
•比DBMS_SQL更易于使用
•比DBMS_SQL需要更少的代码
•通常执行速度比DBMS_SQL快,因为执行的语句较少。