在函数Oracle

时间:2015-05-15 14:50:25

标签: sql oracle partitioning

我们的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(),我们是否会失去分区性能?

1 个答案:

答案 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值和基数。

您的第一个查询会更有效率,因为它可以使用分区键来选择对其执行完整扫描的分区,而第二个则不能并且必须全部扫描它们。