【问题标题】:Display dates in explain plan as literals将解释计划中的日期显示为文字
【发布时间】:2018-11-08 10:47:55
【问题描述】:
SELECT DISTINCT i.name, i.daily_cost
FROM edu_event.items i
WHERE i.purchase_date BETWEEN DATE'2015-01-01' AND DATE'2015-12-31';

对于这个查询,选择了以下执行计划:

----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |  5750 |   213K|   206   (1)| 00:00:01 |
|   1 |  HASH UNIQUE       |       |  5750 |   213K|   206   (1)| 00:00:01 |
|*  2 |   TABLE ACCESS FULL| ITEMS |  5750 |   213K|   205   (1)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("I"."PURCHASE_DATE">=TO_DATE('2015-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "I"."PURCHASE_DATE"<=TO_DATE('2015-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

如您所见,原始日期文字已替换为 to_date 函数。我可以以某种方式禁用它吗?这种格式比较大,但是没有提供更多的信息,所以我不需要。 SQL Developer 可以通过编程方式将其转换回来吗?

【问题讨论】:

    标签: sql oracle oracle-sqldeveloper sql-execution-plan


    【解决方案1】:

    这是 Oracle 在幕后所做的,并不是特殊的 SQL Developer 视图。格式不能修改:

    SQL> explain plan for
      2  with rec (d) as (select sysdate from dual)
      3  select *
      4    from rec
      5   where d > date '2018-01-01';
    
    Explained.
    
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 4034615273
    
    -----------------------------------------------------------------
    | Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
    -----------------------------------------------------------------
    |   0 | SELECT STATEMENT |      |     1 |     2   (0)| 00:00:01 |
    |*  1 |  FILTER          |      |       |            |          |
    |   2 |   FAST DUAL      |      |     1 |     2   (0)| 00:00:01 |
    -----------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    
    PLAN_TABLE_OUTPUT
    -----------------------------------------------------------------------------------------------------------------------------------
    
       1 - filter(SYSDATE@!>TO_DATE(' 2018-01-01 00:00:00', 'syyyy-mm-dd
                  hh24:mi:ss'))
    
    15 rows selected.
    

    可以通过DBMS_XPLAN.DISPLAY()format参数删除谓词信息,但不能修改它的样子

    SQL> select * from table(dbms_xplan.display(null, null, '-PREDICATE'));
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------
    Plan hash value: 4034615273
    
    -----------------------------------------------------------------
    | Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
    -----------------------------------------------------------------
    |   0 | SELECT STATEMENT |      |     1 |     2   (0)| 00:00:01 |
    |   1 |  FILTER          |      |       |            |          |
    |   2 |   FAST DUAL      |      |     1 |     2   (0)| 00:00:01 |
    -----------------------------------------------------------------
    
    9 rows selected.
    

    【讨论】:

      【解决方案2】:

      正如其他答案所述,这是 Oracle 重写 DATEliteral 的方式。

      例如,在使用DATE 字面量定义分区表时,如下所示..

      create table TEST 
      (id NUMBER,
       PART_DATE DATE
      )
      PARTITION BY RANGE (PART_DATE)
      INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
      ( 
         PARTITION p1 VALUES LESS THAN (DATE'2018-08-01'), 
         PARTITION p2 VALUES LESS THAN (DATE'2018-09-01'),
         PARTITION p3 VALUES LESS THAN (DATE'2018-10-01')
      );
      

      ...您将分区的HIGH_VALUES视为to_date函数调用:

      TO_DATE(' 2018-08-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
      TO_DATE(' 2018-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
      TO_DATE(' 2018-10-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')
      

      什么是重要的 - Oracle 了解 日期value

      例如这个查询...

      select * from TEST where PART_DATE = DATE'2018-09-15';
      

      ...产生以下执行计划

      -----------------------------------------------------------------------------------------------
      | Id  | Operation              | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
      -----------------------------------------------------------------------------------------------
      |   0 | SELECT STATEMENT       |      |     1 |    22 |     2   (0)| 00:00:01 |       |       |
      |   1 |  PARTITION RANGE SINGLE|      |     1 |    22 |     2   (0)| 00:00:01 |     3 |     3 |
      |*  2 |   TABLE ACCESS FULL    | TEST |     1 |    22 |     2   (0)| 00:00:01 |     3 |     3 |
      -----------------------------------------------------------------------------------------------
      
      Predicate Information (identified by operation id):
      ---------------------------------------------------
      
         2 - filter("PART_DATE"=TO_DATE(' 2018-09-15 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
      

      注意,FILTER 谓词使用 to_date 函数调用,PstartPstop 指向单个分区 (#3)

      这意味着,Oracle 在基于 DATE 值进行解析时知道将访问哪个分区(并且可以在优化时从该知识中获利)。

      简而言之 - 是的,围绕日期字符串有一个函数调用,但这与对列的函数调用禁止索引访问的情况无法比较。

      相反,当使用谓词PART_DATE = SYSDATE 访问上表时,您会得到一个指向KEY 分区的执行计划。

       |*  2 |   TABLE ACCESS FULL    | TEST |     1 |    22 |     2   (0)| 00:00:01 |   KEY |   KEY |
      

      这意味着 Oracle 知道它只会访问一个分区,但不知道是哪一个。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-07-10
        • 2014-08-02
        • 2016-07-18
        • 2021-01-29
        • 2023-03-10
        • 2013-03-26
        • 2021-08-07
        相关资源
        最近更新 更多