一、静态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 DECLAREtablename VARCHAR2(20); --表名field1 VARCHAR2(20); --字段1名称datatype1 VARCHAR2(20); --字段1类型field2 VARCHAR2(20); --字段2名称datatype2 VARCHAR2(20); --字段2类型str_sql VARCHAR2(500); --拼接SQL语句的字符串 BEGINtablename := 't1';field1:='id';datatype1:='number';field2:='name';datatype2:='varchar(20)';str_sql := 'create table '||tablename||'('||field1 ||' '||datatype1||','||field2 ||' '||datatype2||')';EXECUTE IMMEDIATE str_sql;EXCEPTIONWHEN OTHERS THENDBMS_OUTPUT.put_line('操作失败!'); END;
动态插入数据;
--动态处理费查询的DML语句:向刚才创建的表中插入数据 DECLAREv_id NUMBER; --输入序号;v_name VARCHAR(20); --输入姓名;str_sql VARCHAR2(500); --保存拼接的SQL语句 BEGINv_id := &vid;v_name := '&name';str_sql := 'insert into t1 values(:1,:2)'; --使用占位符代表变量EXECUTE IMMEDIATE str_sqlUSING v_id,v_name; --使用变量替换SQL中的占位符,v_id替换:1,v_name替换:2,依此类推。COMMIT; --执行完毕后直接提交 END;
查询表中的数据有多少行
--处理单行查询的SELECT举例,查询表中的数据有多少行 DECLAREv_count NUMBER;str_sql VARCHAR2(500); BEGINstr_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表中所有的数据。
DECLARETYPE ref_cur IS REF CURSOR;rc ref_cur;emprow emp%ROWTYPE;v_sql VARCHAR2(100):= 'select * from emp where deptno = :x'; --动态执行的SQL语句 BEGINOPEN rc FOR v_sql USING 30; --打开游标,绑定执行的SQL语句,并传递参数 LOOPFETCH 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创建表
DECLAREtablename 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; --行数 BEGINv_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;
向表中插入一条数据:
DECLAREv_id NUMBER := &vid;v_name VARCHAR2(20) := '&vname';v_sql VARCHAR2(100) := 'insert into t2 values(:id,:name)';v_cursor NUMBER;v_row NUMBER; BEGINv_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中的数据
DECLAREV_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; BEGINV_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语句,需要有接受返回值 LOOPEXIT 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;