一、静态SQL和动态SQL的概念。
1、静态SQL
静态SQL是我们常用的使用SQL语句的方式,就是编写PL/SQL时,SQL语句已经编写好了。因为静态SQL是在编写程序时就确定了,我们只能使用SQL中的DML和事务控制语句,但是DDL语句,以及会话控制语句却不能再PL/SQL中直接使用,如动态创建表或者某个不确定的操作时,这就需要动态SQL来实现。
2、 动态SQL
动态SQL是指在PL/SQL编译时SQL语句是不确定的,如根据用户输入的参数的不同来执行不同的操作。编译程序对动态语句部分不进行处理,只是在程序运行时动态创建语句,对语句进行分析,病执行该语句。
静态SQL的优势是性能较高,但不灵活。动态SQL的优势是灵活,缺点是性能稍差。
二、动态创建DML、DDL的SQL语句。
动态创建SQL有一下几类:
1、DDL语句、DCL语句、非查询的DML语句、单行查询的SELECT语句,这类可以使用EXECUTE IMMEDIATE语句执行。
2、多行查询的SELECT语句可以使用游标来实现。
3、通过DBMS_SQL程序包实现。
下面来介绍以上3种情况:
1、使用EXECUTE IMMEDIATE语句处理相关语句:
语法:
EXECUTE IMMEDIATE dynamic_sql_string
[into define_variable_list]
[using bind_argument_list];
例:
动态创建表t1
-- 处理DDL、DCL语句,根据用户输入的表明及字段名动态创建表t1 DECLARE tablename VARCHAR2 ( 20 ); -- 表名 field1 VARCHAR2 ( 20 ); -- 字段1名称 datatype1 VARCHAR2 ( 20 ); -- 字段1类型 field2 VARCHAR2 ( 20 ); -- 字段2名称 datatype2 VARCHAR2 ( 20 ); -- 字段2类型 str_sql VARCHAR2 ( 500 ); -- 拼接SQL语句的字符串 BEGIN tablename : = ' t1 ' ; field1: = ' id ' ; datatype1: = ' number ' ; field2: = ' name ' ; datatype2: = ' varchar(20) ' ; str_sql : = ' create table ' || tablename || ' ( ' || field1 || ' ' || datatype1 || ' , ' || field2 || ' ' || datatype2 || ' ) ' ; EXECUTE IMMEDIATE str_sql; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line( ' 操作失败! ' ); END ;
动态插入数据;
-- 动态处理费查询的DML语句:向刚才创建的表中插入数据 DECLARE v_id NUMBER ; -- 输入序号; v_name VARCHAR ( 20 ); -- 输入姓名; str_sql VARCHAR2 ( 500 ); -- 保存拼接的SQL语句 BEGIN v_id : = & vid; v_name : = ' &name ' ; str_sql : = ' insert into t1 values(:1,:2) ' ; -- 使用占位符代表变量 EXECUTE IMMEDIATE str_sql USING v_id,v_name; -- 使用变量替换SQL中的占位符,v_id替换:1,v_name替换:2,依此类推。 COMMIT ; -- 执行完毕后直接提交 END ;
查询表中的数据有多少行
-- 处理单行查询的SELECT举例,查询表中的数据有多少行 DECLARE v_count NUMBER ; str_sql VARCHAR2 ( 500 ); BEGIN str_sql : = ' select count(*) from t1 ' ; EXECUTE IMMEDIATE str_sql INTO v_count; -- 将查询的结果存放到变量v_count中。 DBMS_OUTPUT.put_line(v_count); END ;
绑定变量的优缺点:
1)可以再库缓存中共享游标,节省了CPU等资源,可以避免额外开销。
2)SQL语句使用绑定变量可以避免被注入攻击。
3)绑定变量是一种减少应用程序在分析查询时使用栓锁数目的可靠方法。
不适合使用变量绑定的情况:
1)对于隔相当长一段时间才执行一次的SQL语句,利用绑定变量的好处hi被不能有效利用而抵消。
2)在数据仓库的情况下。
3)在对建有索引的字段,且字段非常大时,利用绑定变量可能会导致查询计划错误,从而导致查询效率非常低。
实现DDL语句中的注意事项:
PL/SQL块使用动态SQL执行DDL语句的时候与其它不同,在DDL中不能使用绑定变量。
实现DML语句中的注意事项:
不能使用绑定变量替换实际的数据库对象名(表,视图,列等),只能替换字面两,如果对象名在运行时生成的,我们只能使用字符串拼接。
2、通过游标实现多行查询的SELECT语句
REF游标可以处理返回届国际的动态SQL。实现动态SQL的REF游标声明和普通REF游标相同,知识OPEN时绑定的是动态SQL字符串。
例:查询emp表中所有的数据。
DECLARE TYPE ref_cur IS REF CURSOR ; rc ref_cur; emprow emp % ROWTYPE; v_sql VARCHAR2 ( 100 ): = ' select * from emp where deptno = :x ' ; -- 动态执行的SQL语句 BEGIN OPEN rc FOR v_sql USING 30 ; -- 打开游标,绑定执行的SQL语句,并传递参数 LOOP FETCH rc INTO emprow; EXIT WHEN rc % NOTFOUND; dbms_output.put_line( ' name: ' || emprow.ename || ' sal: ' || emprow.sal); END LOOP; CLOSE rc; END ;
3、DBMS_SQL程序包
DBMS_SQL程序包是系统提供给我们的另一种使用动态SQL的方法。程序包中封装了一些列存储过程,帮助我们动态执行SQL。
使用DBMS_SQL包实现动态SQL的步骤如下:
1)将要执行的SQL语句或一个语句块放到一个字符串变量中。
2)使用DBMS_SQL包的parse过程来分析该字符串。
3)使用DBMS_SQL包的bind_variable过程来绑定变量。
4)使用DBMS_SQL包的execute函数来执行语句。
例:使用DBMS_SQL创建表
DECLARE tablename VARCHAR2 ( 20 ) : = ' t2 ' ; -- 表名 field1 VARCHAR2 ( 20 ) : = ' id ' ; -- 字段1名称 datatype1 VARCHAR2 ( 20 ) : = ' number ' ; -- 字段1类型 field2 VARCHAR2 ( 20 ) : = ' name ' ; -- 字段2名称 datatype2 VARCHAR2 ( 20 ) : = ' varchar(20) ' ; -- 字段2类型 v_sql VARCHAR2 ( 500 ) : = ' create table ' || tablename || ' ( ' || field1 || ' ' || datatype1 || ' , ' || field2 || ' ' || datatype2 || ' ) ' ; -- 拼接SQL语句的字符串 v_cursor NUMBER ; -- 定义光标 v_row NUMBER ; -- 行数 BEGIN v_cursor: = dbms_sql.open_cursor; -- 为处理打开光标 dbms_sql.parse(v_cursor,v_sql,dbms_sql.native); -- 分析语句; v_row: = DBMS_SQL. execute (v_cursor); -- 执行sql语句; dbms_sql.close_cursor(v_cursor); -- 关闭光标; DBMS_OUTPUT.put_line(v_row); END ;
向表中插入一条数据:
DECLARE v_id NUMBER : = & vid; v_name VARCHAR2 ( 20 ) : = ' &vname ' ; v_sql VARCHAR2 ( 100 ) : = ' insert into t2 values(:id,:name) ' ; v_cursor NUMBER ; v_row NUMBER ; BEGIN v_cursor: = dbms_sql.open_cursor; dbms_sql.parse(v_cursor,v_sql,dbms_sql.native); dbms_sql.bind_variable(v_cursor, ' :id ' ,v_id); dbms_sql.bind_variable(v_cursor, ' :name ' ,v_name); v_row : = dbms_sql. execute (v_cursor); dbms_sql.close_cursor(v_cursor); COMMIT ; DBMS_OUTPUT.put_line(v_row); END ;
查询EMP中的数据
DECLARE V_DEPTNO NUMBER : = & DEPTNO; V_SQL VARCHAR2 ( 100 ) : = ' select empno,ename,sal from emp where deptno = :deptno ' ; V_CURSOR NUMBER ; V_NO NUMBER ; V_ENAME VARCHAR2 ( 20 ); V_SAL NUMBER ; v_start NUMBER ; BEGIN V_CURSOR : = DBMS_SQL.OPEN_CURSOR; -- 打开游标 DBMS_SQL.PARSE(V_CURSOR, V_SQL, DBMS_SQL.NATIVE); -- 解析动态SQL语句 DBMS_SQL.BIND_VARIABLE(V_CURSOR, ' :deptno ' , V_DEPTNO); -- 传递参数 DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 1 , V_NO); -- 定义输出的列,和查询的列相匹配 DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 2 , V_ENAME, 20 ); DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 3 , V_SAL); v_start : = DBMS_SQL. execute (V_CURSOR); -- 执行SQL语句,需要有接受返回值 LOOP EXIT WHEN DBMS_SQL.FETCH_ROWS(V_CURSOR) <= 0 ; -- 解析游标, DBMS_SQL.COLUMN_VALUE(V_CURSOR, 1 , V_NO); -- 将当前行的数据写入上面对应的列中。 DBMS_SQL.COLUMN_VALUE(V_CURSOR, 2 , V_ENAME); DBMS_SQL.COLUMN_VALUE(V_CURSOR, 1 , V_SAL); DBMS_OUTPUT.PUT_LINE( ' no: ' || V_NO || ' enmae: ' || V_ENAME || ' sal: ' || V_SAL); -- 输出内容 END LOOP; dbms_sql.close_cursor(v_cursor); -- 关闭游标 END ;