从一个案例看PL/SQL代码片的编译与运行
PL/SQL语言是Oracle针对数据库业务逻辑需求开发的一种面向过程的结构化编程语句。在Oracle内核中,存在PL/SLQ引擎和SQL引擎两个重要组成部分,分别用于处理结构化的PL/SQL语句和SQL语句。
同所有高级语言一样,PL/SQL语句同样存在编译和运行两个关键步骤。在Compile环节,主要实现语法权限检查、对象方法检查和语法结构检查。在运行Runtime阶段,相同的过程其实还是会进行,一些语句错误都是在运行时发生检测。
本篇主要通过一个错误案例,详细分析一下两者的特点和差异。
1、问题说明
朋友向笔者咨询一个问题,为什么写好的PL/SQL匿名块在执行状态时,报错没有被exception所捕获。说明:基于数据保护原因,代码片段为模拟版本。
实验环境为11gR2,具体版本号为11.2.0.4。
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 – Production
模拟代码片段如下:
SQL> declare
2 i varchar2(10);
3 begin
4 select err
5 into i
6 from t
7 where rownum<2;
8 exception
9 when others then
10 dbms_output.put_line('Errors Catch!');
11 end;
12 /
select err
*
ERROR at line 4:
ORA-06550: line 4, column 10:
PL/SQL: ORA-00904: "ERR": invalid identifier
ORA-06550: line 4, column 3:
PL/SQL: SQL Statement ignored
朋友的问题是,数据表T的确没有err列,但是执行的时候为什么没有在exception部分被捕获,而是直接报错了呢?
数据表T的描述如下:
SQL> desc t;
Name Type Nullable Default Comments
------- ------------------ -------- ------- --------
USER_ID VARCHAR2(100 BYTE) Y
2、解析与测试
这个问题直观看比较唬人,但是仔细分析起来还是有概念障碍的问题。这就是程序的编译时和运行时。
我们编写PL/SQL程序的时候,无论是procedure还是package,都有一个显示compile的动作,成功之后我们才可以exec执行程序。在compile的时候,所进行的工作对于任何语言编译器来讲,都是差不多的。
常见的大多数都是验证和校验,比如使用变量是否定义?该用户有无使用对象、变量权限?变量可见性范围?或者语法使用是否正确?经过compile之后,编译器会认为程序已经具备了执行的前提条件,会将其转化为目标对象。
但是,经过编译的程序,执行过程(也就是运行时Runtime)就万无一失吗?肯定不是,内存、CPU、磁盘的资源约束,内在逻辑操作的错误、数字运算的错误都会引起一系列的运行时故障。我们说,PL/SQL中的exception,就是针对runtime的错误而言的。
一种假设:错误使用的是一个匿名块。在执行过程中,实际上是编译和运行两步走的情况。程序还没有进入runtime阶段,都被编译compile发现了错误,抛了出来。
我们通过一系列简单实验,来证明结论。首先,创建一个单独的存储过程,不执行只是编译,看结果如何?
SQL> create or replace procedure TEST
2 as
3 i varchar2(10);
4 begin
5 select err
6 into i
7 from t
8 where rownum<2;
9 exception
10 when others then
11 dbms_output.put_line('Errors Catch!');
12 end;
13 /
Warning: Procedure created with compilation errors.
编译错误,通过show error显示:
SQL> show error
Errors for PROCEDURE TEST:
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/3 PL/SQL: SQL Statement ignored
5/10 PL/SQL: ORA-00904: "ERR": invalid identifier
错误信息和匿名块的完全相同。此时并没有进入运行时,可以推想匿名块的报错是在编译时发生的。
那么怎么能够回避编译时检查,将这个错误“送到”运行时,让exception捕获到呢?我们可以使用字符串。
SQL> create or replace procedure TEST
2 as
3 i varchar2(10);
4 begin
5 execute immediate 'select err from t where rownum<2'
6 into i;
7
8 exception
9 when others then
10 dbms_output.put_line('Errors Catch!');
11 end;
12 /
Procedure created.
execute immediate可以直接执行字符串类SQL语句。该存储过程成功创建,说明PL/SQL引擎没有进行针对字符串的检查。
执行:
SQL> set serveroutput on;
SQL> exec test;
Errors Catch!
PL/SQL procedure successfully completed.
果然可以被exception捕获,同预想效果相同。根据这个思路,修改朋友的代码如下:
SQL> declare
2 i varchar2(10);
3 begin
4 execute immediate 'select err from t where rownum<2'
5 into i;
6
7 exception
8 when others then
9 dbms_output.put_line('Errors Catch!');
10 end;
11 /
Errors Catch!
PL/SQL procedure successfully completed.
顺利通过编译Compile检查,到Runtime时候报错。
3、结论
PL/SQL语句是我们进行数据库结构化、过程化处理的重要工具。应该意识到,PL/SQL本身也是一种编程语言,都需要Compile和Runtime阶段,每个阶段有不同的任务使命。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17203031/viewspace-1962370/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/17203031/viewspace-1962370/
- Win10上编译Caffe之Libcaffe,运行mnist案例
- Java整个编译以及运行的过程
- 运行QTP测试脚本后,将编译结果写入指定文件(二)
- sphinx4下载、编译、运行
- 命令行下面创建Rhodes程序项目Hello World,编译并启动BlackBerry模拟器运行程序
- Tensor on Android学习笔记(一) ----在Windows下只用Android Studio编译运行TensorFlow Android demo
- 在ultraedit中设置编译,运行命令
- 案例学习:如何让你的SQL运行得更快
- hadoop32位编译为64位以及安装运行注意事项--centos7
- Ubuntu 16.04 Sublime Text3 Java编译运行(最简单的方法)
- MFC:“Debug Assertion Failed!” ——自动生成的单文档程序项目编译运行就有错误
- 命令行中编译与运行带有包的java文件
- 在gem5的full system下运行 x86编译的测试程序 running gem5 on ubuntu in full system mode in x86
- 能够编译,为啥就是不能运行通过???
- GNU ARM汇编--(二)汇编编译链接与运行
- VC中编译、运行程序的小知识点
- 编译运行Android模拟器
- ubuntu 下编译运行C/C++语言
- Github安卓开源项目编译运行
- Mac Android 源码下载 编译 运行 Android6.0.1