我们的Oracle数据库中有一个使用此语法的分区表:
...
PARTITION BY RANGE(saledate)
(PARTITION sal99q1 VALUES LESS THAN (TO_DATE('01-APR-1999', 'DD-MON-YYYY')),
PARTITION sal99q2 VALUES LESS THAN (TO_DATE('01-JUL-1999', 'DD-MON-YYYY')),
...
我们通常在select语句中使用分区键,如下所示:
Select * from table where saledate >= trunc(sysdate-3) and saledate < trunc(sysdate-2)
要使用更少的代码获得相同的结果,我通常会使用此查询:
Select * from table where trunc(saledate) = trunc(sysdate-3)
我的问题是,通过在函数中使用分区键,在这种情况下使用trunc(),我们是否会失去分区性能?
答案 0 :(得分:1)
您误解了Toad所显示的计划(在您的回答中)。对于显示的两个查询,分别为:
Partition #: 2 Partitions determined by key values
和
Partition #: 1 Partitions accessed #1 - #17
第一个查询是根据键值(即日期)仅访问所需的分区;所以它只需要对可能包含日期的分区进行全面扫描。
第二个查询必须访问所有分区,因为您正在使用函数操作键值,这意味着您不再使用分区键。关键是saledate
,而不是trunc(saledate)
。这类似于在索引列上使用函数时发生的情况;在这种情况下不再使用索引,此处不再使用分区键。正如你似乎怀疑你的问题,是的,你确实失去了效率。
您还可以看到基数因为函数调用而被猜为50,而不是统计提供的值4966。
您可以使用dbms_xplan
从虚拟表中看到相同的内容;来自您的第一个查询:
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4996 | 39968 | 14 (0)| 00:00:01 | | |
|* 1 | FILTER | | | | | | | |
| 2 | PARTITION RANGE ITERATOR| | 4996 | 39968 | 14 (0)| 00:00:01 | KEY | KEY |
|* 3 | TABLE ACCESS FULL | T42 | 4996 | 39968 | 14 (0)| 00:00:01 | KEY | KEY |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TRUNC(SYSDATE@!-3)<TRUNC(SYSDATE@!-2))
3 - filter("SALEDATE">=TRUNC(SYSDATE@!-3) AND "SALEDATE"<TRUNC(SYSDATE@!-2))
从第二个问题开始:
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50 | 400 | 14 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE ALL| | 50 | 400 | 14 (0)| 00:00:01 | 1 | 17 |
|* 2 | TABLE ACCESS FULL | T42 | 50 | 400 | 14 (0)| 00:00:01 | 1 | 17 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(TRUNC(INTERNAL_FUNCTION("SALEDATE"))=TRUNC(SYSDATE@!-3))
注意每个查询中的pstart / pstop值和基数。
您的第一个查询会更有效率,因为它可以使用分区键来选择对其执行完整扫描的分区,而第二个则不能并且必须全部扫描它们。