gpt4 book ai didi

Oracle 根据 to_date(literal) 的格式使用或忽略索引列

转载 作者:行者123 更新时间:2023-12-04 15:22:29 27 4
gpt4 key购买 nike

我使用一个索引列作为过滤器,将它放在两个文字值之间。 (该列位于索引的第二个位置,实际上使执行速度变慢;我稍后会处理)。

让我感到困惑的是 Oracle (11.2.0.3.0) 根据提供给 to_date 的值和格式字符串的格式使用或忽略所述索引:

这忽略了索引:

SQL> SELECT *
2 FROM gprs_history_import gh
3 WHERE start_call_date_time BETWEEN
4 to_date('20140610 000000','yyyymmdd hh24miss') AND
5 to_date('20140610 235959','yyyymmdd hh24miss')
6 /

Execution Plan
----------------------------------------------------------
Plan hash value: 990804809

--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 350 | 219K| 242K (1)| 00:56:42 | | |
| 1 | PARTITION RANGE SINGLE| | 350 | 219K| 242K (1)| 00:56:42 | 74 | 74 |
| 2 | PARTITION LIST ALL | | 350 | 219K| 242K (1)| 00:56:42 | 1 | 3 |
|* 3 | TABLE ACCESS FULL | GPRS_HISTORY_IMPORT | 350 | 219K| 242K (1)| 00:56:42 | 220 | 222 |
--------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("START_CALL_DATE_TIME"<=TO_DATE(' 2014-06-10 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))

这个确实使用了索引(注意第 4 行中日期部分后面的空格):
SQL> SELECT *
2 FROM gprs_history_import gh
3 WHERE start_call_date_time BETWEEN
4 to_date('20140610 ','yyyymmdd ') AND
5 to_date('20140610 235959','yyyymmdd hh24miss')
6 /

Execution Plan
----------------------------------------------------------
Plan hash value: 464458373

---------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 350 | 219K| 2795K (1)| 10:52:15 | | |
|* 1 | FILTER | | | | | | | |
| 2 | PARTITION RANGE ITERATOR | | 350 | 219K| 2795K (1)| 10:52:15 | KEY | 74 |
| 3 | PARTITION LIST ALL | | 350 | 219K| 2795K (1)| 10:52:15 | 1 | 3 |
| 4 | TABLE ACCESS BY LOCAL INDEX ROWID| GPRS_HISTORY_IMPORT | 350 | 219K| 2795K (1)| 10:52:15 | KEY | 222 |
|* 5 | INDEX SKIP SCAN | GPRS_HISTORY_IMPORT_IDX1 | 1 | | 2795K (1)| 10:52:15 | KEY | 222 |
---------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(TO_DATE('20140610 ','yyyymmdd ')<=TO_DATE(' 2014-06-10 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))
5 - access("START_CALL_DATE_TIME">=TO_DATE('20140610 ','yyyymmdd ') AND "START_CALL_DATE_TIME"<=TO_DATE(' 2014-06-10
23:59:59', 'syyyy-mm-dd hh24:mi:ss'))
filter("START_CALL_DATE_TIME">=TO_DATE('20140610 ','yyyymmdd ') AND "START_CALL_DATE_TIME"<=TO_DATE(' 2014-06-10
23:59:59', 'syyyy-mm-dd hh24:mi:ss'))

((1)中的过滤器好像有点傻,好像oracle没看懂表达式)

同样,这个没有(我删除了尾随空格):
SQL> SELECT *
2 FROM gprs_history_import gh
3 WHERE start_call_date_time BETWEEN
4 to_date('20140610','yyyymmdd') AND
5 to_date('20140610 235959','yyyymmdd hh24miss')
6 /

Execution Plan
----------------------------------------------------------
Plan hash value: 990804809

--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 350 | 219K| 242K (1)| 00:56:42 | | |
| 1 | PARTITION RANGE SINGLE| | 350 | 219K| 242K (1)| 00:56:42 | 74 | 74 |
| 2 | PARTITION LIST ALL | | 350 | 219K| 242K (1)| 00:56:42 | 1 | 3 |
|* 3 | TABLE ACCESS FULL | GPRS_HISTORY_IMPORT | 350 | 219K| 242K (1)| 00:56:42 | 220 | 222 |
--------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

3 - filter("START_CALL_DATE_TIME"<=TO_DATE(' 2014-06-10 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))

在空格周围加上引号会阻止索引被使用。

是什么赋予了?

最佳答案

好的 - 我会尝试一下,这主要是从可用信息中扣除的:

Oracle 为什么选择不同的执行计划?

在您使用不寻常的日期格式的第二个查询中,优化器似乎不知道结果日期的值是什么。您会看到过滤谓词:
1 - filter(TO_DATE('20140610 ','yyyymmdd ')<=TO_DATE(' 2014-06-10 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))
这意味着优化器甚至不确定第一个日期是否小于第二个!这意味着优化器不知道返回的行数,只会使用通用计划而不考虑特定的统计信息。如果您有一个用户定义的函数 xyt() 将返回范围的日期,情况也是如此。优化器无法知道会产生什么日期值 - 这意味着您将获得一个通用的通用计划,对于指定的任何日期范围来说,这应该是相当不错的。

在第一种和第三种情况下,优化器似乎直接了解日期,并且可以通过统计来猜测日期范围内的行数。因此,虽然第二个查询是对优化器的,如 BETWEEN X AND 3这个查询就像 BETWEEN 1 AND 3于是他针对预测的返回行数优化了查询计划!

奇怪的事情似乎是,查询优化器在奇怪的日期格式方面有这样的问题,可以作为错误/改进请求提交......

但重要的一点:

  • 全表扫描不一定是一个糟糕的计划......以及使用索引并不总是更快!
  • 查询计划中的成本与实际执行时间或性能没有任何直接关系 - 它是比较相同查询的不同计划的内部度量(因此您无法比较不同查询的成本,例如查询 1,2 和3)

  • 基本上,如果您从表中返回大量行,则在许多情况下,没有索引访问的全表扫描会快得多,尤其是在某些分区上操作时! - 表扫描将仅访问匹配日期范围的分区 - 因此仅针对相关日期并返回该分区中的所有行。这比查询每一行的索引然后通过索引访问提取行要快得多......尝试分析查询 - 分区上的全表扫描应该快 3 倍,IO 少得多

    关于Oracle 根据 to_date(literal) 的格式使用或忽略索引列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24253731/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com