设为首页 收藏本站
查看: 890|回复: 0

[经验分享] oracle 11g PL/SQL Programming学习九

[复制链接]

尚未签到

发表于 2016-8-4 09:35:14 | 显示全部楼层 |阅读模式
----------------------------------------------------------------------------

-----------------PL/SQL学习笔记系列 By Cryking-----------------

------------------------转载请注明出处,谢谢!------------------------

  

第三部分 PL/SQL 高级编程

第11章 动态SQL(Dynamic SQL)

NDS(native Dynamic SQL)在9i中出现,10g和11g中得到提高.它提供了一个可替代DBMS_SQL的方法.

NDS和DBMS_SQL包让你可以在运行时创建和执行动态sql.

本章分为三大部分:

动态SQL结构

NDS

DBMS_SQL包


动态sql语句是非常有用的技术,它让你在PL/SQL程序运行时可以创建和执行查询.

在使用动态sql语句中,NDS是首先选择的.

大部分时候都可以使用NDS来解决,只有当动态语句的输出值的数量和数据类型未知时,你才必须使用DBMS_SQL包来解决.

动态SQL结构(Dynamic SQL Architecture)

动态SQL可以灵活的解决很多问题.它可以让你写匿名函数.

匿名函数的声明和正常函数一样,但是它们有未知的参数列表和返回类型.

当你有NDS和DBMS_SQL两种方法时,你应该先考虑NDS,因为dbms_sql只是为了向下兼容而存在的.

动态sql的结构中,你可以使用字符串连接或者使用占位符.使用字符串连接容易受到SQL注入攻击.

而使用占位符可以避免这种SQL注入攻击.这些占位符就是绑定变量.它们就像是动态SQL中的形式参数.

编译时,将不会对动态语句中的对象进行验证.

运行动态语句的过程包含四个步骤:

1.在运行时的语句被解析

2.将有占位符的语句映射实际参数到形式参数

3.执行语句

4.返回值给调用语句



本地动态SQL(Native Dynamic SQL (NDS))

NDS是一个有用并且简单的工具,它非常容易使用和部署.



动态语句

使用动态DDL语句,可以在编译时避免失败,因为编译时不检查动态语句中的对象及语法.

示例1:
--删除某个对象,如使用静态语句,当此对象不存在时,会报错
--使用动态SQL则避免了抛出错误
00:34:43 SCOTT@orcl> BEGIN
00:34:45   2    -- Use a loop to check whether to drop a sequence.
00:34:45   3    FOR i IN (SELECT null
00:34:45   4                FROM user_objects
00:34:45   5               WHERE object_name = 'SAMPLE_SEQUENCE') LOOP
00:34:45   6      EXECUTE IMMEDIATE 'DROP SEQUENCE sample_sequence';
00:34:45   7      dbms_output.put_line('Dropped [sample_sequence].');
00:34:45   8    END LOOP;
00:34:45   9  END;
00:34:46  10  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00
00:34:49 SCOTT@orcl> create SEQUENCE sample_sequence;

Sequence created.

Elapsed: 00:00:00.03
00:35:08 SCOTT@orcl> BEGIN
00:35:15   2    -- Use a loop to check whether to drop a sequence.
00:35:15   3    FOR i IN (SELECT null
00:35:15   4                FROM user_objects
00:35:15   5               WHERE object_name = 'SAMPLE_SEQUENCE') LOOP
00:35:15   6      EXECUTE IMMEDIATE 'DROP SEQUENCE sample_sequence';
00:35:15   7      dbms_output.put_line('Dropped [sample_sequence].');
00:35:15   8    END LOOP;
00:35:15   9  END;
00:35:17  10  /
Dropped [sample_sequence].

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.09


DML动态语句

DML动态语句经常是在运行时进行拼接.字符串拼接带来的问题就是sql注入.

使用DBMS_ASSERT包可以对输入参数进行验证以防SQL注入攻击.

示例:
  

21:20:34 SCOTT@orcl> create table emp_bak as select * from emp;
Table created.
Elapsed: 00:00:01.16
21:21:38 SCOTT@orcl> CREATE OR REPLACE PROCEDURE insert_item(table_name VARCHAR2,
21:21:40   2                                          pename     varchar2) IS
21:21:40   3    stmt VARCHAR2(2000);
21:21:40   4  BEGIN
21:21:40   5    --动态DML语句拼接,使用dbms_assert防止sql注入
21:21:40   6    stmt := 'INSERT INTO ' || dbms_assert.simple_sql_name(table_name) ||
21:21:40   7            '(EMPNO,ENAME) SELECT MAX(EMPNO)+1,' ||
21:21:40   8            dbms_assert.enquote_literal(pename) || ' FROM ' ||
21:21:40   9            dbms_assert.simple_sql_name(table_name);
21:21:40  10    dbms_output.put_line(stmt);
21:21:40  11    EXECUTE IMMEDIATE stmt;
21:21:40  12  END insert_item;
21:21:40  13  /

Procedure created.

Elapsed: 00:00:00.18
21:21:41 SCOTT@orcl> BEGIN
21:21:50   2    insert_item(table_name => 'EMP', pename => 'CryTest');
21:21:50   3  END;
21:21:51   4  /
INSERT INTO EMP(EMPNO,ENAME) SELECT MAX(EMPNO)+1,'CryTest' FROM EMP

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.04
21:21:51 SCOTT@orcl> select empno,ename from emp where ename='CryTest';

EMPNO ENAME
---------- ----------
7935 CryTest

1 row selected.

Elapsed: 00:00:00.01

  

--dbms_assert.simple_sql_name简单的检查名称是否符合sql命名,它不检查长度
21:22:40 SCOTT@orcl> BEGIN insert_item(table_name => '00sss', pename => 'CryTest
_ERR'); END;
21:23:11   2  /
BEGIN insert_item(table_name => '00sss', pename => 'CryTest_ERR'); END;
*
ERROR at line 1:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 146
ORA-06512: at "SCOTT.INSERT_ITEM", line 6
ORA-06512: at line 1


Elapsed: 00:00:00.04



其中dbms_assert.enquote_literal给字符串首尾自动加上单引号,并对字符串里的单引号进行验证.(验证单引号必须成对出现)


关于SQL注入攻击

SQL注入攻击就是尝试以不成对的引号字符串传入到SQL语句中.

动态SQL就是一个黑客可以试图利用你的代码的地方.

oracle现在使用DBMS_ASSERT包来帮助你来防止SQL注入攻击.

它有以下的函数:

1.ENQUOTE_LITERAL

它接受一个字符串输入,给字符串首尾加上单引号后返回.

2.ENQUOTE_NAME

它将一个字符串首尾加上双引号之后返回.它有一个可选的参数控制是否需要将输入的字符串转换为大写.

3.NOOP

不进行任何验证,输入是什么,返回就是什么
21:43:47 SCOTT@orcl> select dbms_assert.noop('input Test') a from dual;

A
-------------------------
input Test

1 row selected.

Elapsed: 00:00:00.01


4.QUALIFIED_SQL_NAME

它验证输入的字符串是否是一个有效的模式对象名称.
21:44:00 SCOTT@orcl> select dbms_assert.QUALIFIED_SQL_NAME('emp01') col from dual;

COL
-----------------
emp01

1 row selected.

Elapsed: 00:00:00.01
21:46:39 SCOTT@orcl> select dbms_assert.QUALIFIED_SQL_NAME('01emp') col from dual;
select dbms_assert.QUALIFIED_SQL_NAME('01emp') col from dual
*
ERROR at line 1:
ORA-44004: invalid qualified SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 188


Elapsed: 00:00:00.01





5.SCHEMA_NAME

它验证输入字符串是否是一个有效的模式名称.
21:48:25 SCOTT@orcl> select dbms_assert.SCHEMA_NAME('emp') col from dual;
select dbms_assert.SCHEMA_NAME('emp') col from dual
*
ERROR at line 1:
ORA-44001: invalid schema
ORA-06512: at "SYS.DBMS_ASSERT", line 243


Elapsed: 00:00:00.01
21:48:34 SCOTT@orcl> select dbms_assert.SCHEMA_NAME('SCOTT') A from dual;

A
----------
SCOTT

1 row selected.

Elapsed: 00:00:00.01



6.SIMPLE_SQL_NAME

它简单验证输入的字符串是否是一个有效的模式对象名称.

7.SQL_OBJECT_NAME

它验证输入的字符串是否是一个有效的模式对象名.
21:53:31 SCOTT@orcl> select dbms_assert.SQL_OBJECT_NAME('empEMP01') col from dual;
select dbms_assert.SQL_OBJECT_NAME('empEMP01') col from dual
*
ERROR at line 1:
ORA-44002: invalid object name
ORA-06512: at "SYS.DBMS_ASSERT", line 293


Elapsed: 00:00:00.02
21:53:55 SCOTT@orcl> select dbms_assert.SIMPLE_SQL_NAME('empEMP01') col from dual;

COL
------------
empEMP01

1 row selected.

Elapsed: 00:00:00.00
21:54:31 SCOTT@orcl> select dbms_assert.SQL_OBJECT_NAME('emp') A from dual;

A
----------
emp

1 row selected.

Elapsed: 00:00:00.01

当使用绑定变量的时候,不需要使用DBMS_ASSERT包来防止SQL注入攻击.


带占位符的动态语句

占位符就像是形式参数,它们零散的分布在动态sql语句里面.你使用USING子句来向它们传递实际参数值.

USING子句可带以逗号隔开的参数列表.它们缺省情况下是IN模式(传值模式).

你可以手动指定IN OUT或OUT模式.

oracle 11g文档对占位符变量有几点建议:

1.如果动态的SELECT语句返回的结果最多一行,你应该通过INTO子句来返回值.

这可能需要你以引用游标的方式来打开这个语句,或者将SQL语句放到一个带括号的匿名块中.

(OPEN FOR 游标 或者 OPEN FOR (动态SQL))

如果返回的值后面要用到,你不能在USING子句中使用IN OUT或OUT模式的参数.

2.如果动态的SELECT语句返回的结构多于一行,你应当通过BULK COLLECT INTO子句来返回值.

3.如果是动态的DML语句,并且只有输入的占位符,你应该将它们放到USING子句中.

4.如果是动态的DML语句,并且使用了RETURING INTO子句,你应该将输入的值放到USING子句中,将输出的值放到RETURNING INTO子句中.

5.如果是动态的PL/SQL匿名块或CALL语句,你应该在USING子句中使用输入和输出参数.

此时你应该根据需要将USING子句中的参数设置为IN OUT模式或OUT模式或IN模式.

示例:
23:40:52 SCOTT@orcl> CREATE OR REPLACE PROCEDURE insert_item(pempno number, pename varchar2) IS
23:40:53   2    stmt VARCHAR2(2000);
23:40:53   3    --使用占位符
23:40:53   4  BEGIN
23:40:53   5    stmt := 'INSERT INTO emp_bak '
23:40:53   6    || '(EMPNO,ENAME) '
23:40:53   7    || 'SELECT :EMPNO,:ENAME'
23:40:53   8    || ' FROM DUAL';
23:40:53   9    dbms_output.put_line(stmt);
23:40:53  10    EXECUTE IMMEDIATE stmt
23:40:53  11      USING pempno, pename;
23:40:53  12  END insert_item;
23:40:55  13  /

Procedure created.

Elapsed: 00:00:00.10
23:40:56 SCOTT@orcl> BEGIN insert_item(pempno => 9999, pename => 'CryTest'); END
;
23:43:42   2  /
INSERT INTO emp_bak (EMPNO,ENAME) SELECT :EMPNO,:ENAME FROM DUAL

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.02



与上面的使用字符串拼接的方法有所不同的是,这里使用了绑定变量.

USING中的实际参数个数应当要与动态SQL中的占位符个数一致.
应当注意表名等不能使用绑定变量.


  一般情况下,我们应当首选绑定变量的这种方式来执行动态SQL.
  带输入和输出的动态语句

使用NDS可以非常方便绑定输入和获得输出.而使用DBMS_SQL方法将会是很冗长的.

示例:


23:54:52 SCOTT@orcl> DECLARE
23:55:08   2    -- Define explicit record structure.
23:55:08   3    TYPE emp_record IS RECORD(
23:55:08   4      empno number,
23:55:08   5      ename VARCHAR2(60));
23:55:08   6    -- Define dynamic variables.
23:55:08   7    emp_cursor SYS_REFCURSOR;
23:55:08   8    emp_row    emp_record;
23:55:08   9    stmt       VARCHAR2(2000);
23:55:08  10  BEGIN
23:55:08  11    -- Set statement.
23:55:08  12    stmt := 'SELECT empno, ename FROM emp_bak ' ||
23:55:08  13            'WHERE ename = :input';
23:55:08  14    -- Open and read dynamic cursor, then close it.
23:55:08  15    OPEN emp_cursor FOR stmt
23:55:08  16      USING 'SCOTT';
23:55:08  17    LOOP
23:55:08  18      FETCH emp_cursor
23:55:08  19        INTO emp_row;
23:55:08  20      EXIT WHEN emp_cursor%NOTFOUND;
23:55:08  21      dbms_output.put_line('[' || emp_row.empno || '][' || emp_row.ename || ']');
23:55:08  22    END LOOP;
23:55:08  23    CLOSE emp_cursor;
23:55:08  24  END;
23:55:09  25  /
[7788][SCOTT]

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.01



--使用BULK COLLECT INTO处理返回多行的动态SELECT语句
00:02:47 SCOTT@orcl> DECLARE
00:03:17   2    -- Define explicit record structure.
00:03:17   3    TYPE emp_record IS RECORD(
00:03:17   4      empno number,
00:03:17   5      ename VARCHAR2(60));
00:03:17   6    TYPE emp_collection IS TABLE OF emp_record;
00:03:17   7    -- Define dynamic variables.
00:03:17   8    emp_cursor SYS_REFCURSOR;
00:03:17   9    emp_rows   emp_collection;
00:03:17  10    stmt       VARCHAR2(2000);
00:03:17  11  BEGIN
00:03:17  12    -- Set statement.
00:03:17  13    stmt := 'SELECT empno, ename ' || 'FROM emp_bak ' ||
00:03:17  14            'WHERE SUBSTR(ename,0,1) = :input';
00:03:17  15    -- Open and read dynamic cursor, then close it.
00:03:17  16    OPEN emp_cursor FOR stmt USING 'S';
00:03:17  17    FETCH emp_cursor BULK COLLECT INTO emp_rows;
00:03:17  18    for i in 1 .. emp_rows.count loop
00:03:17  19      dbms_output.put_line('[' || emp_rows(i).empno || '][' || emp_rows(i).ename || ']');
00:03:17  20    END LOOP;
00:03:17  21    CLOSE emp_cursor;
00:03:17  22  END;
00:03:18  23  /
[7369][SMITH]
[7788][SCOTT]

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.11





--使用CLOB的动态SQL语句

--注意这种更新CLOB字段的方法
00:12:10 SCOTT@orcl> CREATE TABLE ITEM AS
00:12:31   2  SELECT 1 ITEM_ID
00:12:31   3  ,'TEST_TITLE' item_title
00:12:31   4  ,TO_CLOB('Just for Test') item_desc FROM DUAL;

Table created.

Elapsed: 00:00:00.08
00:15:43 SCOTT@orcl> DECLARE
00:15:44   2    -- Define explicit record structure.
00:15:44   3    target CLOB;
00:15:44   4    source VARCHAR2(2000) := 'FOR APPEND TEST!';
00:15:44   5    movie  VARCHAR2(60) := 'TEST_TITLE';
00:15:44   6    stmt   VARCHAR2(2000);
00:15:44   7  BEGIN
00:15:44   8    -- Set statement.
00:15:44   9    stmt := 'UPDATE item ' || 'SET item_desc = empty_clob() ' ||
00:15:44  10            ' WHERE item_title = :input ' ||
00:15:44  11            'RETURNING item_desc INTO :descriptor';
00:15:44  12    EXECUTE IMMEDIATE stmt
00:15:44  13      USING movie
00:15:44  14      RETURNING INTO target;--因为empty_clob(),所以target返回是空的
--下面才把要更新的值放到target里,达到真正的更新字段的目的
00:15:44  15    dbms_lob.writeappend(target, LENGTH(source), source);
00:15:44  16  END;
00:15:45  17  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.14
00:16:26 SCOTT@orcl> col item_desc format a18
00:16:32 SCOTT@orcl> select *  from item;

ITEM_ID ITEM_TITLE ITEM_DESC
---------- ---------- ------------------
1 TEST_TITLE FOR APPEND TEST!

1 row selected.

Elapsed: 00:00:00.01


  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-252572-1-1.html 上篇帖子: oracle定时执行存储过程的job 下篇帖子: Oracle数据库中大型表查询优化的研究
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表