惯例先上oracle数据库版本信息:
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE 9.2.0.1.0 Production
TNS for 32-bit Windows: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production
Oracle数据库一直在更新,具体版本不同,Oracle可能会产生一些小的变动,所以下面的测试案例全部都是在我的Oracle 9i版本上测试成功的,在9i以上版本不确定,如有不同,欢迎评论补充!!!
众所周知,Oracle数据库执行一条SQL语句的顺序是:
1.语法分析,这阶段主要是判断有没语法错误,比如常见的拼写错误以及逻辑错误,比如:select少写个e变成slect,这个就是在语法分析阶段检查出来的
2.语义分析,这阶段主要是检查SQL语句中访问的对象是否存在以及该用户是否具备相应的权限,比如:select * from TEST;如果数据库中没有表TEST或者该用户没有权限访问该表,这个就是在语义分析阶段检查出来的
3.对SQL语句进行解析,利用内部算法进行解析,生成解析数以及执行计划
4.执行SQL语句,返回执行结果
首先从语法分析阶段入手,语法分析阶段是从右往左进行的,下面举个简单的例子来证明一下:
SQL> select * from emp where 1/0=1 and 1 = 2;
未选定行
SQL> select * from emp where 1 = 2 and 1/0=1;
select * from emp where 1 = 2 and 1/0=1
*
ERROR 位于第 1 行:
ORA-01476: 除数为 0
简要分析一下:第一个SQL语句之所以返回“未选定行”,是因为执行这个SQL语句时从右往左进行,先判断1 = 2这个显而易见的不成立,而逻辑判断符是and,所以直接忽略前面的条件:1/0=1,条件不成立直接返回“未选定行”;
而第二个SQL语句为什么报错呢?同理是因为他从右往左进行语法分析,先判断1/0=1这个条件,显然0不能作为除数,所以直接报错:ORA-01476:除数为0;
显然,这个例子证明了语法分析阶段是从右往左进行的,如果是从左往右进行,那么返回的结果应该是第一句SQL语句返回ORA-01476:除数为0,第二句返回“未选定行”。
语义分析阶段主要检查SQL语句中访问的对象是否存在以及该用户是否具备相应的权限,实在想不出啥比较好的例子能够证明语义分析阶段的执行顺序,暂且跳过。
接下来分析一下SQL语句执行时where字句的执行顺序:
SQL> desc test;
名称 是否为空? 类型
----------------------------------------- -------- ----------------------------
T1 VARCHAR2(10)
T2 VARCHAR2(10)
SQL> select * from test;
T1 T2
---------- ----------
hl abc
yy 1
yy 2
SQL> select * from test where to_number(t2) > 1 and t1 = 'yy';
T1 T2
---------- ----------
yy 2
SQL> select * from test where t1 = 'yy' and to_number(t2) > 1;
select * from test where t1 = 'yy' and to_number(t2) > 1
*
ERROR 位于第 1 行:
ORA-01722: 无效数字
简要解释一下同样的筛选条件,不同的顺序,执行结果却完全不同的原因:
首先第一个没有报错的SQL语句为什么正确而且有结果呢?其实是因为where语句是自下而上执行,从右往左执行,依据这个顺序,首先通过t1 = 'yy'过滤掉第一行记录,只剩yy----1和yy----2这两行记录,然后通过to_number(t2) > 1这个条件过滤掉yy----1这行记录,所以返回结果如上。
再来看看为什么第二行会报错呢?同样还是因为where字句的执行顺序造成的。首先先判断条件:to_number(t2) > 1,先对Test这个表进行全表扫描,由于第一条记录是:hl----abc,显然abc不可能通过to_number()强制转化成数字,所以直接报错了。
如果觉得这个例子还不能让你信服,再来看看下面的例子:
create or replace function f1 (v_in_name varchar2)
return varchar2
as
begin
dbms_output.put_line ('f1的v_in_name:'||v_in_name);
return v_in_name;
exception
when no_data_found then
dbms_output.put_line ('出现异常');
end;
/
create or replace function f2 (v_in_name varchar2)
return varchar2
as
begin
dbms_output.put_line ('f2的v_in_name:'||v_in_name);
return v_in_name;
exception
when no_data_found then
dbms_output.put_line ('出现异常');
end;
/
select 1 from dual where f1('1')=1 and f2('1')=1;
select 1 from dual where f2('1')=1 and f1('1')=1;
通过执行这俩SQL语句的输出结果可以判断出where字句确实是从右往左执行的。(我电脑oracle版本是9i,版本较低,无法贴出输出结果,但我在同学10g版本上测试过,执行第一个SQL语句先输出f2的v_in_name:1然后再输出f1,第二个与之相反)。