oracle - Oracle 解析器如何处理序列

标签 oracle sequence

为什么oracle总是解析下面的查询?

select MY_SEQUENCE_SEQ.nextval
from dual

来自 Quest SQL Optimizer (8.6.0) 的 SGA 统计信息:

Executions : 83630

Parse_calls: 83630

序列详细信息:

  • 最后缓存值:1
  • 增加:1
  • 缓存大小:20
  • 周期:否
  • 订单:否

测试场景:

  1. 创建音序器:

    CREATE SEQUENCE MY_SEQUENCE_SEQ
      START WITH 1
      MAXVALUE 999999999999999999999999999
      MINVALUE 1
      NOCYCLE
      CACHE 20
      NOORDER;
    
  2. 对有权访问 v$sql View 的用户执行此查询。

    select executions, 
           parse_calls 
      from v$sql 
     where sql_text like 'select MY_SEQ%';`
    
  3. 按序列执行查询 n 次

  4. 从第 2 点开始执行查询。

得到的结果:

EXECUTIONS  - n
PARSE_CALSS - n

测试于:

数据库:Oracle Database 10g 版本 10.2.0.4.0 - 64 位生产

客户端:Toad 版本 11.5.1.2

最佳答案

这不是 Oracle 的错误,这只是 TOAD 将 SQL 发送到 Oracle 的方式而已。即toad 不会缓存oracle 的语句句柄,它只是在完成后关闭它。

当查询发送到 SQL 引擎时,Oracle 将对查询执行以下三项主要操作之一。

  1. 硬解析它
  2. 软解析
  3. 不解析它

即我们希望处于情况 3,但我们当然不希望处于情况 1! 那么每种情况会在什么时候发生呢?

当 SQL 根本不在共享池中或者 SQL 在共享池中但正在使用的绑定(bind)变量/文字意味着当前 SQL 不可用时,将发生硬解析。例如,假设我们发出此 SQL 三次 select MY_SEQUENCE_SEQ.nextval from Dual。这将在 Oracle 第一次看到此 SQL 并将其放入共享池时进行硬解析,并在第二次和第三次调用时进行软解析。我们可以看到这很容易发生:

SQL> select n.name, s.value from v$mystat s, v$statname n where n.statistic# = s.statistic# and n.name in ('parse count (hard)', 'parse count (total)');

NAME                      VALUE
-------------------- ----------
parse count (total)         522
parse count (hard)          287

SQL> select /* test1 */ MY_SEQUENCE_SEQ.nextval
  2  from dual;

   NEXTVAL
----------
        62

SQL> select /* test1 */ MY_SEQUENCE_SEQ.nextval
  2  from dual;

   NEXTVAL
----------
        63

SQL> select /* test1 */ MY_SEQUENCE_SEQ.nextval
  2  from dual;

   NEXTVAL
----------
        64

SQL> select n.name, s.value from v$mystat s, v$statname n where n.statistic# = s.statistic# and n.name in ('parse count (hard)', 'parse count (total)');

NAME                      VALUE
-------------------- ----------
parse count (total)         526
parse count (hard)          288

SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'select /* test1 */%';

SQL_TEXT                       EXECUTIONS PARSE_CALLS
------------------------------ ---------- -----------
select /* test1 */ MY_SEQUENCE          3           3
_SEQ.nextval from dual

硬解析加了 1,sql 已注册 3 个解析,因此 1 个硬解析(将其放入共享池)和 2 个软解析。

为什么要软解析?为了发生“不解析”,客户端代码必须保留语句句柄并重新执行它。 也就是说,如果我们用 Java 编写,我们会这样写:

    public static int getNextSeq(String str)
  throws Exception 
    {
        if (sel == null)
      {
        sel = con.prepareStatement("select MY_SEQUENCE_SEQ.nextval v from dual "+str);
      }
    ResultSet rs = sel.executeQuery();
    int seqVal=0;
    while (rs.next()) 
    {
      seqVal = rs.getInt("V");
    }
    return seqVal;
    }

即如果我们还没有这样做的话,我们只调用PrepareStatement。如果我们执行这段代码

System.out.println(getNextSeq(args[0]));
System.out.println(getNextSeq(args[0]));
System.out.println(getNextSeq(args[0]));

我们可以看到它的实际效果:

SQL> host java Prep two
70
71
72

SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'select %two';

SQL_TEXT                       EXECUTIONS PARSE_CALLS
------------------------------ ---------- -----------
select MY_SEQUENCE_SEQ.nextval          3           1
 v from dual two

现在,除了 1 次硬解析之外,oracle 没有解析 SQL。如果 Java 代码写得不好,我们会看到这样的情况:

sel = con.prepareStatement("select MY_SEQUENCE_SEQ.nextval v from dual "+str);
ResultSet rs = sel.executeQuery();


SQL> host java Prep three
73
74
75

SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'select %three';

SQL_TEXT                       EXECUTIONS PARSE_CALLS
------------------------------ ---------- -----------
select MY_SEQUENCE_SEQ.nextval          3           3
 v from dual three

现在我们看到解析计数 = 执行次数。换句话说,我们对每个调用进行软解析,这并不理想。再说一次,这不是 Oracle 的限制,只是糟糕的客户端实现。

使用 PL/SQL,我们不必担心这个问题。为什么? PL/SQL 不会解析任何一个,因为它针对运行 SQL 进行了很多优化(毫不奇怪!)。 例如:

SQL> declare
  2    v_seq number;
  3  begin
  4    for idx in 1..3 loop
  5      select MY_SEQUENCE_SEQ.nextval into v_seq from dual pls_test;
  6    end loop;
  7  end;
  8  /

PL/SQL procedure successfully completed.

SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'SELECT %PLS_TEST';

SQL_TEXT                       EXECUTIONS PARSE_CALLS
------------------------------ ---------- -----------
SELECT MY_SEQUENCE_SEQ.NEXTVAL          3           1
 FROM DUAL PLS_TEST

现在,pl/sql 为我们做这一优化有一个需要注意的地方,那就是参数 SESSION_CACHED_CURSORS。在给定的 session 中,Oracle 将为我们保持打开一组游标(即它们是软打开的,也就是说,如果我们需要更多游标,它将关闭它们)。因此,如果我们设置 SESSION_CACHED_CURSORS=0 并重复上述测试,我们将看到软解析突然蔓延:

SQL> alter session set session_cached_cursors=0;

Session altered.

SQL> declare
  2    v_seq number;
  3  begin
  4    for idx in 1..3 loop
  5      select MY_SEQUENCE_SEQ.nextval into v_seq from dual pls_test2;
  6    end loop;
  7  end;
  8  /

PL/SQL procedure successfully completed.

SQL> select sql_text, executions, parse_calls from v$sql where sql_text like 'SELECT %PLS_TEST2';

SQL_TEXT                       EXECUTIONS PARSE_CALLS
------------------------------ ---------- -----------
SELECT MY_SEQUENCE_SEQ.NEXTVAL          3           3
 FROM DUAL PLS_TEST2

显然,缓存游标的值越高,我们就越有可能避免软解析并达到完全避免解析的 chalice 。

关于oracle - Oracle 解析器如何处理序列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13826105/

相关文章:

oracle - PostgreSQL 物化 View

mysql - Oracle 分区的替代方案

sql - 找出每学期和每门类(class)中得分最高的学生的名字?

Vim:如何在函数中从 "01"计数到 "02"(不是 "01"> "2")?

oracle - 在 SQL 数据库中存储一个范围/多个范围

java - Java 中的 Oracle 查询

r - 如何将 "NA"添加到具体标识位置的向量中?

postgresql - Postgres : Link sequence date virtual table to real table

oracle - hibernate oracle 序列产生大的间隙

postgresql - 跳过 postgresql 序列中的某些范围?