【问题标题】:Query Performance while using Oracle aggregate function使用 Oracle 聚合函数时的查询性能
【发布时间】:2013-04-16 20:22:50
【问题描述】:

下面是我使用聚合函数的查询。 where 子句很简单,在 corpId 和 incoming_date 上有一个索引。 如果我只是获取所有行/计数,则查询时间不到一秒。但是,当我使用聚合函数时,查询大约需要 4 分钟。 我正在使用 oracle 11i,where 子句检索的总行数约为 64000。最近还收集了表和索引统计信息,表中没有添加新行。

请提出提高速度的建议。

SELECT
sum(paid_amt) totalamount
FROM test_table e
WHERE  e.corpId =6
AND e. incoming_date >= to_date('01-12-2012','dd-mm-yyyy')
AND e. incoming _date <= to_date('09-01-2013','dd-mm-yyyy')

【问题讨论】:

  • 您正在运行的两个查询是什么?特别是,当您不使用聚合函数时,您是否选择了paid_amt 列?这两个查询计划是什么?您是说返回 64,000 行中的最后一行需要不到一秒钟的时间吗?还是您使用的 GUI 会在获取最后一行之前显示前几行?
  • 这两个查询:第一个是我已经发布的那个,第二个是获取计数的那个(使用 count(*))。计数在不到一秒的时间内给出 64k,而总和需要 4 分钟。

标签: performance oracle function aggregate


【解决方案1】:

paid_amt 包含到索引中:

CREATE INDEX
        ix_testtable_cord_date_paid
ON      test_table (corpId, incoming_date, paid_amt)

如果您只有(corpId, incoming_date) 的索引并尝试像这样测试速度:

SELECT  COUNT(*)
FROM    test_table
WHERE   e.corpId = 6
        AND e.incoming_date >= to_date('01-12-2012','dd-mm-yyyy')
        AND e.incoming_date <= to_date('09-01-2013','dd-mm-yyyy')

您不会查询索引之外的任何内容,因此仅使用 INDEX (RANGE SCAN) 即可满足查询。

只要您添加了索引中没有的任何内容(在您的情况下为paid_amt),查询就需要使用额外的TABLE ACCESS (BY INDEX ROWID) 从表中检索记录。

它是嵌套循环中的随机查找,速度很慢,尤其是如果您的表记录很大(有很多字段或很长的字段)。

优化器甚至可能认为这种访问方法的效率低于FULL SCAN,并改用后者。

【讨论】:

  • 是的。谢谢。我正在(corpId、incoming_date、paid_amt)上创建复合索引。该表有 319,332,896 条记录。所以创建索引需要一些时间。将在创建索引后发布更新。
  • 感谢您的建议。它运作良好。总和需要 0.1 秒。如果我将查询更改为以下查询会怎样:
  • SELECT NVL(SUM(paid_amt),0)paidAmount, NVL(SUM(cust_served),0) totalCustServed, NVL(SUM(BILLED_AMT,0)) billedAmt, NVL(SUM(e.tax_paid, 0)) taxPaid, COUNT(DISTINCT cust_name) custName, NVL(SUM(bal_Payment,0)) balPayment, NVL(SUM(adv_payment,0)) advPayment FROM test_table e WHERE 1=1 AND e.corp_id =92 AND e.incoming_date >= '02-Aug-2012' AND e.incoming__date
  • 以下索引是否有效? CREATE INDEX TT_PAID_IDX ON test_table corp_id,incoming_date,paid_amt,cust_served, BILLED_AMT,tax_paid,cust_name,bal_Payment,adv_payment) 表空间 TIDX
【解决方案2】:

我像这样重新创建了这个场景......

CREATE TABLE test_table 
  ( id integer
  , corpId integer
  , paid_amt number(10,2)
  , incoming_date DATE );

ALTER TABLE test_table
add CONSTRAINT test_table_pk PRIMARY KEY (id);

create index test_table_nui_1 on test_table(corpId);

create index test_table_nui_2 on test_table(incoming_date);

create sequence test_table_seq;

insert into test_table
  select test_table_seq.nextval
       ,MOD(test_table_seq.currval,6)
       ,MOD(test_table_seq.currval,10) + 1
       ,sysdate - MOD(test_table_seq.currval,200) 
  from all_objects, user_objects;  

all_objects 和 user_objects 之间的笛卡尔连接只是一种快速插入大量记录的技巧。 (本例中为 657,000 行)

首先运行所有 657,000 个选择...

select sum(paid_amt)
from test_table;

计划 SELECT STATEMENT ALL_ROWS成本:621 字节:13 基数:1
2 SORT AGGREGATE 字节:13 基数:1
1 表访问全表 DAVE.TEST_TABLE 成本:621 字节:9,923,914 基数:763,378

然后 109,650 个 corpId...

select sum(paid_amt)
from test_table
where corpId = 5;

计划 SELECT STATEMENT ALL_ROWS成本:265 字节:26 基数:1
3 SORT AGGREGATE 字节:26 基数:1
2 按索引 ROWID 表访问表 DAVE.TEST_TABLE 成本:265 字节:3,310,138 基数:127,313
1 INDEX RANGE SCAN INDEX DAVE.TEST_TABLE_NUI_1 成本:213 基数:3,054

最后是 20,836 行,按日期限制...

SELECT sum(paid_amt) totalamount
FROM test_table e
WHERE  e.corpId = 5
AND e. incoming_date >= to_date('01-12-2012','dd-mm-yyyy')
AND e. incoming_date <= to_date('09-01-2013','dd-mm-yyyy')

计划 SELECT STATEMENT ALL_ROWS成本:265 字节:35 基数:1
3 SORT AGGREGATE 字节:35 基数:1
2 按索引 ROWID 表访问表 DAVE.TEST_TABLE 成本:265 字节:871,360 基数:24,896
1 INDEX RANGE SCAN INDEX DAVE.TEST_TABLE_NUI_1 成本:213 基数:3,054

所有 3 个查询都很快速(即

另一种方法是删除 nui_1 和 nui_2 并在两列上创建组合索引。然后在我的数据库上运行了 31 毫秒

create index test_table_nui_3 on test_table(corpId, incoming_date);

计划 SELECT STATEMENT ALL_ROWSCost:15 字节:35 基数:1
3 SORT AGGREGATE 字节:35 基数:1
2 按索引 ROWID 表访问表 DAVE.TEST_TABLE 成本:15 字节:871,360 基数:24,896
1 INDEX RANGE SCAN INDEX DAVE.TEST_TABLE_NUI_3 成本:3 基数:14

这表明聚合函数不是问题,但您的索引可能是问题。最好的办法是检查您的解释计划。

【讨论】:

  • 我已经在(corpId,incoming_date)上有了一个复合索引。问题是 test_table 本身有 319,332,896 条记录。我指定的 where 子句获取 64k 记录(使用计数(*)),由于复合索引,速度很快(不到一秒)。计算这 64k 条记录的 sum(paid_amt) 需要很长时间。
  • 抱歉没有发现这张桌子这么大。
  • 您可以考虑对 corp_id 和/或日期进行分区,或者您可以使用物化视图。即使在我将行数翻倍之后,一个 MV 也将我的示例缩短到只有 15 毫秒。
猜你喜欢
  • 2011-10-27
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多