我正在使用包含大约30列和15亿行的旧Oracle表。它包含过去两年的销售数据。该表具有SALES_DATE,CLIENT_ID和PRODUCT_ID的索引。我经常需要在两个日期之间找出特定客户的特定产品的销售价值。我经常运行的查询类型为:
select sum(SALES_VALUE)
from SALES
where CLIENT_ID = 9999
and PRODUCT_ID IN (1, 2, 15, 16)
and SALES_DATE between to_date('2015-01-01', 'yyyy-mm-dd')
and to_date('2015-02-28', 'yyyy-mm-dd')
此查询的单次运行通常需要半小时(即使选择了相对较短的日期范围),我很难理解原因。查询本身是否有任何特别低效的问题,还是更可能是由于数据库本身存在性能问题? 我改变数据库本身的能力非常有限,但我可以自由编写自己的查询。我该怎么做才能提高性能?
提前致谢, OSF
编辑: 我们使用的是Oracle Database 11g 11.2.0.1.0 以下是解释计划的结果:
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Pstart| Pstop | TQ |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 20 | 11189 | | | | | |
| 1 | SORT AGGREGATE | | 1 | 20 | | | | | | |
| 2 | PX COORDINATOR | | | | | | | | | |
| 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | 20 | | | | Q1,00 | P->S | QC (RAND) |
| 4 | SORT AGGREGATE | | 1 | 20 | | | | Q1,00 | PCWP | |
| 5 | PX PARTITION RANGE ALL | | 157 | 3140 | 11189 | 1 | 160 | Q1,00 | PCWC | |
| 6 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES | 157 | 3140 | 11189 | 1 | 160 | Q1,00 | PCWP | |
| 7 | INDEX RANGE SCAN | IX_SALES_DATE | 295K| | 703 | 1 | 160 | Q1,00 | PCWP | |
-------------------------------------------------------------------------------------------------------------------------------------
答案 0 :(得分:1)
SUM要求数据库在显示结果之前读取并处理满足查询条件的所有行,并且优化程序认为最大限制条件是SALES_DATE。它似乎也在SALES_DATE分区,这也可能影响优化器的决定。
首先,通过执行
确保表的统计信息是最新的BEGIN
DBMS_STATS.GATHER_TABLE_STATS('YOUR_SCHEMA', 'SALES');
END;
您可能需要让DBA为您运行此操作。希望您的桌面上已经定期收集统计数据,因此请咨询您的DBA。您可以通过执行
找出上次收集的统计数据SELECT TABLE_NAME, LAST_ANALYZED
FROM USER_TABLES
WHERE TABLE_NAME = 'SALES'
如果这已经过了一段时间(超过几天),或者这是一个高活动表,则可能需要更频繁的统计数据收集。
正如上面评论中提到的,为了提高性能,您可能需要添加索引。您可以在(SALES_DATE,CLIENT_ID,PRODUCT_ID)上添加索引,但仍然需要数据库在扫描索引后读取实际数据行,将所需的I / O加倍,这可能会提示优化器进行表扫描 - 所以如果你是务实的,你可以制作索引(SALES_DATE,CLIENT_ID,PRODUCT_ID,SALES_VALUE),它允许优化器获得查询所需的所有数据而无需读取行中的实际数据。这是一个提高性能的实用解决方案,但可能(通常)减慢INSERT和UPDATE。
祝你好运。