Ben*_*Ben 18 sql oracle plsql sequence oracle11g
是否在文档中所描述的短路评价CASE和COALESCE()在SQL中使用时适用于序列?这似乎没有发生.
Oracle数据库使用短路评估.对于一个简单的
CASE表达式...如果先前的comparison_expr等于expr,Oracle从不评估comparison_expr.对于搜索的CASE表达式,数据库...如果先前条件为真,则永远不会评估条件.
同样,COALESCE() 文档说明:
Oracle数据库使用短路评估.数据库计算每个expr值并确定它是否为NULL,而不是在确定它们中的任何值是否为NULL之前评估所有expr值.
从SQL调用序列时,似乎不是这种情况; 正如您所看到的,不会发生短路并且序列会增加.
SQL> create sequence tmp_test_seq start with 1 increment by 1;
SQL> select tmp_test_seq.nextval from dual;
   NEXTVAL
----------
         1
SQL> select tmp_test_seq.currval from dual;
   CURRVAL
----------
         1
SQL> select coalesce(1, tmp_test_seq.nextval) from dual;
COALESCE(1,TMP_TEST_SEQ.NEXTVAL)
--------------------------------
                               1
SQL> select tmp_test_seq.currval from dual;
   CURRVAL
----------
         2
SQL> select case when 1 = 1 then 1 else tmp_test_seq.nextval end as s from dual;
         S
----------
         1
SQL> select tmp_test_seq.currval from dual;
   CURRVAL
----------
         3
但是,从PL/SQL调用时,序列不会递增:
SQL> create sequence tmp_test_seq start with 1 increment by 1;
SQL> declare
  2     i number;
  3  begin
  4     i := tmp_test_seq.nextval;
  5     dbms_output.put_line(tmp_test_seq.currval);
  6     i := coalesce(1, tmp_test_seq.nextval);
  7     dbms_output.put_line(i);
  8     dbms_output.put_line(tmp_test_seq.currval);
  9     i := case when 1 = 1 then 1 else tmp_test_seq.nextval end;
 10     dbms_output.put_line(i);
 11     dbms_output.put_line(tmp_test_seq.currval);
 12  end;
 13  /
1
1
1
1
1
SQL> select tmp_test_seq.nextval from dual;
   NEXTVAL
----------
         2
从PL/SQL调用SQL中的序列与SQL的结果相同:
SQL> create sequence tmp_test_seq start with 1 increment by 1;
SQL> declare
  2     i number;
  3  begin
  4     select tmp_test_seq.nextval into i from dual;
  5     dbms_output.put_line(tmp_test_seq.currval);
  6     select coalesce(1, tmp_test_seq.nextval) into i from dual;
  7     dbms_output.put_line(i);
  8     dbms_output.put_line(tmp_test_seq.currval);
  9     select case when 1 = 1 then 1 else tmp_test_seq.nextval end into i
 10       from dual;
 11     dbms_output.put_line(i);
 12     dbms_output.put_line(tmp_test_seq.currval);
 13  end;
 14  /
1
1
2
1
3
文档中似乎没有任何关于此的内容; 管理员指南管理序列,对序列psuedocolumns SQL语言参考,关于和CURRVAL NEXTVAL的PL/SQL语言参考或序列的数据库概念的概述.
在SQL中使用时,是否对序列进行短路评估CASE和COALESCE()发生?这记录了吗?
如果感兴趣,我们的目标是11.2.0.3.5.
对于PL/SQL,Oracle确保它将使用短路评估:
在评估逻辑表达式时,PL/SQL使用短路评估.也就是说,PL/SQL一旦确定结果就会停止评估表达式.因此,您可以编写可能导致错误的表达式.
当您使用nextvalin SQL代码时,我们有不同的情况.
首先,我们必须记住,currval并且nextval是伪列:
伪列的行为类似于表列,但实际上并不存储在表中.您可以从伪列中进行选择,但不能插入,更新或删除它们的值.伪列也类似于没有参数的函数(请参阅第5章"函数".但是,不带参数的函数通常为结果集中的每一行返回相同的值,而伪列通常为每一行返回不同的值.
来自:3个Pseudocolumns.
现在的问题是:为什么Oracle评估nextval?或者这种行为是否在某处?
在包含对NEXTVAL的引用的单个SQL语句中,Oracle将序列递增一次:
对于SELECT语句的外部查询块返回的每一行.这样的查询块可以出现在以下位置:
- 顶级SELECT语句
- INSERT ... SELECT语句(单表或多表).对于多表插入,对NEXTVAL的引用必须出现在VALUES子句中,并且对于子查询返回的每一行,序列都会更新一次,即使可以在多表插入的多个分支中引用NEXTVAL.
- CREATE TABLE ... AS SELECT语句
- CREATE MATERIALIZED VIEW ...作为SELECT语句
对于UPDATE语句中更新的每一行
对于包含VALUES子句的每个INSERT语句
对于MERGE语句合并的每一行.对NEXTVAL的引用可以出现在merge_insert_clause或merge_update_clause或两者中.对于每个更新的行以及插入的每一行,NEXTVALUE值都会递增,即使在更新或插入操作中实际未使用序列号也是如此.如果在任何这些位置中指定了NEXTVAL多次,则序列对于每一行递增一次,并为该行的所有NEXTVAL返回相同的值.
来自:序列伪列
您的情况显然是"1.顶级SELECT语句",但这并不意味着短路逻辑不到位,而只是nextval总是评估它.
如果您对短路逻辑感兴趣,那么最好nextval从等式中删除.
像这样的查询不会评估子查询:
select 6 c
  from dual
where  'a' = 'a' or 'a' = (select dummy from dual) 
但是如果尝试做类似的事情,coalesce或者case我们会看到Oracle Optimizer决定执行子查询:
select 6 c
  from dual
where  'a' = coalesce('a', (select dummy from dual) )
我在SQLFiddle的这个演示中创建了带注释的测试来展示这个.
它看起来只有当用OR条件,如Oracle应用短路逻辑,但coalesce和case它必须评估所有分支.
我认为你在PL/SQL中的第一次测试表明,coalsce并且在PL/SQL中case使用短路逻辑,正如Oracle所述.您的第二个测试(包括SQL语句中的序列)表明,在这种情况下nextval,即使未使用结果,也会进行评估,Oracle也会对其进行记录.
两件事情放在一起看上去有些奇怪,因为coalesce和case行为似乎是真的太不一致我也,但我们也要记住,该逻辑的实现依赖于实现(在这里我的源)
为什么短路评估不适用于序列的解释可能如下。什么是序列?抛开内部因素不谈,它是序列定义(数据字典表中的记录)和一些内部SGA组件的组合seq$,它不是一个函数,可以考虑,尽管文档没有直接将其声明为行源(但执行计划这样做了)。每次在查询的选择列表中直接引用序列时,优化器在搜索最佳执行计划时都必须对其进行评估。在形成最佳执行计划的过程中,如果nextval引用伪列,则序列会递增:
SQL> create sequence seq1;
Sequence created
这是我们的顺序:
SQL> select o.obj#
  2       , o.name
  3       , s.increment$
  4       , s.minvalue
  5       , s.maxvalue
  6       , s.cache
  7    from sys.seq$ s
  8    join sys.obj$ o
  9       on (o.obj# = s.obj#)
 10    where o.name = 'SEQ1'
 11  ;
      OBJ# NAME    INCREMENT$   MINVALUE   MAXVALUE      CACHE
---------- ------- ---------- ---------- ---------- ----------
     94442 SEQ1             1          1       1E28         20
让我们跟踪下面的查询,并看看它的执行计划
SQL> ALTER SESSION SET EVENTS '10046 trace name context forever, level 4';
Session altered
SQL> select case
  2           when 1 = 1 then 1
  3           when 2 = 1 then seq1.nextval
  4         end as res
  5    from dual;
       RES
----------
         1
/* sequence got incremented by 1 */
SQL> select seq1.currval from dual;
   CURRVAL
----------
         3
跟踪文件信息:
STAT #1016171528 id=1 cnt=1 pid=0 pos=1 obj=94442 op='SEQUENCE SEQ1 ...
STAT #1016171528 id=2 cnt=1 pid=1 pos=1 obj=0 op='FAST DUAL 。 ..
CLOSE #1016171528:c=0,e=12,dep=0,type=0,tim=12896600071500 /* 关闭光标 */
执行计划将向我们展示基本相同的内容:
SQL> explain plan for select case
  2                            when 1 = 1 then 1
  3                            else seq1.nextval
  4                          end
  5                      from dual
  6  /
Explained
Executed in 0 seconds
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
---------------------------------------------------------------
Plan hash value: 51561390
-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     1 |     2   (0)| 00:00:01 |
|   1 |  SEQUENCE        | SEQ1 |       |            |          |
|   2 |   FAST DUAL      |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------
9 rows selected
Executed in 0.172 seconds
在评估方面,直接在查询中引用序列,与包含相关子查询大致相同。该相关子查询将始终由优化器评估:
SQL> explain plan for select case
  2                            when 1 = 1 then 1
  3                            when 2 = 1 then (select 1
  4                                               from dual)
  5                          end as res
  6                      from dual;
Explained
Executed in 0 seconds
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------
Plan hash value: 1317351201
-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     1 |     4   (0)| 00:00:01 |
|   1 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
|   2 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------
9 rows selected
Executed in 0.063 seconds  
我们可以看到该dual表已被包含在执行计划中两次。
子查询的类比是仓促做出的。当然,差异多于相似之处。序列是完全不同的机制。但是,优化器将序列视为行源,只要它没有看到顶级查询列表nextval中直接引用序列的伪列,它就不会评估该序列,否则select无论是否使用短路评估逻辑,序列都会递增。显然,PL/SQL 引擎(从 Oracle 11g r1 开始)有不同的方式来访问序列值。应该注意的是,在以前的 11gR1 版本的 RDBMS 中,我们应该编写一个查询来引用 PL/SQL 块中的序列,该查询由 PL/SQL 引擎直接发送到 SQL 引擎。       
“为什么序列在优化器生成执行计划期间会递增”问题的答案在于序列的内部实现。
| 归档时间: | 
 | 
| 查看次数: | 2557 次 | 
| 最近记录: |