将解释计划中的日期显示为文字

Display dates in explain plan as literals

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 开发人员可以通过编程将其转换回来吗?

这是 Oracle 在幕后所做的,并不是一个特殊的 SQL 开发人员视图。无法修改格式:

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参数去掉predicate信息,但是不能修改它的样子

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.

如其他答案所述,这就是 Oracle 重写 DATE 文字的方式。

例如,在使用 DATE 文字定义分区 table 时,如下所示..

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 访问上述 table 时,您会得到一个指向 KEY 分区的执行计划。

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

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