Oracle SQL 索引 DATE 与索引 TRUNC(DATE)

Oracle SQL Index DATE vs Index TRUNC(DATE)

我有一个名为DEXTRACTO的Table,需要根据F_EXTRACTO列查询一段时间(为DATE格式)具有 BETWEEN DATE1 AND DATE2 条件(DATE1DATE2 可以更改)。这是 table:

的一些数据
SQL> SELECT MIN(F_EXTRACTO), MAX(F_EXTRACTO), COUNT(1)
  2    FROM DEXTRACTO
  3  /

MIN(F_EXTRACTO) MAX(F_EXTRACTO)   COUNT(1)
--------------- --------------- ----------
03/01/2005      06/01/2017        13772806


SQL> SELECT COUNT(1) FROM DEXTRACTO WHERE F_EXTRACTO IS NULL
  2  /

  COUNT(1)
----------
         0

SQL> 

我想使用索引,但我不知道哪种方法更好。我应该在 F_EXTRACTO 列上使用它吗?或者我应该在 TRUNC(F_EXTRACTO) 上使用索引吗? 我知道将索引与函数一起使用不是一个好主意,但是测试这两种方法我得到了这个...

SQL> create index INDEX_DATE on DEXTRACTO (F_EXTRACTO)
  2  /

Index created

SQL> create index INDEX_TRUNC on DEXTRACTO (TRUNC(F_EXTRACTO))
  2  /

Index created

SQL> 

F_EXTRACTO 上的测试索引:

SQL> explain plan for
  2  
  2  SELECT /*+ index (dextracto INDEX_DATE)  */ *
  3    FROM dextracto
  4   WHERE f_extracto
  5         BETWEEN to_date('01/01/2005','dd/mm/yyyy') AND SYSDATE
  6  /

Explained

SQL> select plan_table_output from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost  |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |    12M|  1088M|   250K|
|   1 |  FILTER                      |            |       |       |       |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEXTRACTO  |    12M|  1088M|   250K|
|   3 |    INDEX RANGE SCAN          | INDEX_DATE |    12M|       | 36972 |
---------------------------------------------------------------------------
Note
-----
   - 'PLAN_TABLE' is old version

13 rows selected

SQL> 

TRUNC(F_EXTRACTO) 上的测试索引:

SQL> explain plan for
  2  
  2  SELECT /*+ index (dextracto INDEX_TRUNC) */ *
  3    FROM dextracto
  4   WHERE TRUNC(f_extracto)
  5         BETWEEN to_date('01/01/2005','dd/mm/yyyy') AND SYSDATE
  6  /

Explained

SQL> select plan_table_output from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
----------------------------------------------------------------------------
| Id  | Operation                    | Name        | Rows  | Bytes | Cost  |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |             | 32437 |  2787K|  1130 |
|   1 |  FILTER                      |             |       |       |       |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEXTRACTO   | 32437 |  2787K|  1130 |
|   3 |    INDEX RANGE SCAN          | INDEX_TRUNC | 58387 |       |   169 |
----------------------------------------------------------------------------
Note
-----
   - 'PLAN_TABLE' is old version

13 rows selected

SQL> 

所以...如果我使用索引 F_EXTRACTO 成本是 250000,但如果我使用索引 TRUNC(F_EXTRACTO) 成本是 1130。有人能告诉我为什么两者之间存在如此大的差异两种方法?如果您需要一些其他信息,请告诉我。

估计行数(12M 与 58387)之间的巨大差异很可能是由于过时的统计数据造成的。我建议在添加索引后收集统计信息(例如,使用 DBMS_STATS.gather_table_stats )。

此外,EXPLAIN PLAN 不保证计划就是实际使用的计划。我宁愿 运行 查询,然后用 dbms_xplan.display_cursor 检查实际执行计划。查看 v$sql/v$sqlarea 视图以了解执行细节也很有意义。

I want to use an index but I don't know wich approach is better.

您肯定不会使用索引访问table中的所有1400万行(如您的示例所示)。 收集统计信息后,在没有提示的情况下重试解释计划,您将看到 FULL TABLE SCAN 以比索引访问更低的成本进行访问。 根据成本 INDEX / FTS 的比率,您可以估计 table 中值得通过索引访问的部分。

要访问长达几个月的时间,INDEX ACCESS 可能更有效,但高于某个阈值时,FULL SCAN 会更好(同时检查解释计划和执行 - 这可能会产生不同的结果)。

在您的用例中,我看不到使用 FBI 有任何好处。不利的一面是不确定的顺序,仅支持每日间隔的粒度。