在函数 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 值和基数。
您的第一个查询会更有效率,因为它可以使用分区键来选择对哪些分区进行全面扫描,而第二个查询不能而且必须扫描所有分区。
我们的 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 值和基数。
您的第一个查询会更有效率,因为它可以使用分区键来选择对哪些分区进行全面扫描,而第二个查询不能而且必须扫描所有分区。