CASE and COALESCE short-circuit evaluation works with sequences in PL/SQL but not in SQL

后端 未结 2 778
孤城傲影
孤城傲影 2020-12-28 17:03

Does the short-circuit evaluation described in the documentation for CASE and COALESCE() apply to sequences when used in SQL? This does not appear

2条回答
  •  旧巷少年郎
    2020-12-28 17:33

    Explanation of why the short-circuit evaluation does not apply to sequences might be the following. What is a sequence? Putting internals aside, it's a combination of sequence definition(record in seq$ data dictionary table) and some internal SGA component, it's not a function and might be considered, although the documentation does not state it directly(but execution plan does) as row source. And every time a sequence is being referenced directly in the select list of a query, it has to be evaluated by the optimizer when it searches for optimal execution plan. During the process of forming an optimal execution plan a sequence gets incremented if nextval pseudocolumn is referenced:

    SQL> create sequence seq1;
    Sequence created
    

    Here is our sequence:

    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
    

    Lets trace below query, and also take a look at its execution plan

    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
    

    Trace file information:

    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 /* close the cursor */

    The execution plan will show us basically the same:

    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
    

    In terms of evaluation, referencing a sequence directly in a query, roughly the same as including a correlated sub-query. That correlated sub-query will always be evaluated by the optimizer:

    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  
    

    We can see that dual table has been included in the execution plan twice.

    The analogy with a sub-query was made in a rush. There are more differences than similarities, of course. Sequences are absolutely different mechanisms. But, a sequences are viewed by the optimizer as a row source, and as long as it doesn't see the nextval pseudocolumn of a sequence being directly referenced in the select list of a top-level query, it won't evaluate the sequence, otherwise sequence will be incremented, whether a short-circuit evaluation logic is being used or not. PL/SQL engine,obviously, (starting from Oracle 11g r1) has a different way to access a sequence value. Should be noted that in previous 11gR1 versions of RDBMS we should write a query to reference a sequence in PL/SQL block, which PL/SQL engine sent directly to the SQL engine.

    The answer to the "why a sequence gets incremented during generating an execution plan by the optimizer" question, lies in the internal implementation of sequences.

提交回复
热议问题