在函数 Oracle 中使用分区键

Using partition key in function Oracle

我们的 Oracle 数据库中有一个分区 table,使用以下语法:

...
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(),我们是否会降低分区性能?

您误解了 Toad 显示的计划(在您的回答中)。对于显示的两个查询,分别为:

Partition #: 2 Partitions determined by key values

Partition #: 1 Partitions accessed #1 - #17

第一个查询是根据键值(即日期)仅访问它需要的分区;所以它只需要对可能包含你的日期的分区进行全面扫描。

第二个查询必须访问所有分区,因为您正在使用函数操作键值,这意味着您不再真正使用分区键。关键是 saledate,而不是 trunc(saledate)。这类似于在索引列上使用函数时发生的情况;在那种情况下不再使用索引,这里也不再使用分区键。正如您似乎从您的问题中怀疑的那样,是的,您确实会降低效率。

您还可以看到,由于函数调用,基数被猜测为 50,而不是 stats 提供的值 4966。

您可以使用 dbms_xplan 从虚拟 table 看到相同的东西;来自您的第一个查询:

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

您的第一个查询会更有效率,因为它可以使用分区键来选择对哪些分区进行全面扫描,而第二个查询不能而且必须扫描所有分区。