2015-04-12 208 views
3

我有一个简单的查询正在运行。有一个日期条件,我一旦删除,查询就返回结果。它的格式为'31 -MAR-15'的日期字段。我不明白为什么这种情况会使查询速度变慢。提前致谢。Oracle - 查询运行速度很慢

SELECT 
    substr(a.id, 1, 2) AS country, 
    count(DISTINCT a.id) AS id_count, 
    sum(a.amount)  AS amount 
FROM table1 a 
    JOIN table2 b ON a.id = b.id 
    JOIN table3 c ON b.party_id = c.party_id 
WHERE a.prod_type = 'INS' 
    AND c.acct_type = 'LON' 
    AND substr(a.id, 1, 2) = 'US' 
    AND a.dump_dt = '31-MAR-15' 
    AND substr(id, 4, 8) = '20150303' 
GROUP BY substr(a.id, 1, 2); 

解释计划:

PLAN_TABLE_OUTPUT 
Plan hash value: 255044277 

------------------------------------------------------------------------------------------------------------ 
| Id | Operation       | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT     |      |  1 | 121 | 125K (1)| 00:25:08 | 
| 1 | HASH GROUP BY     |      |  1 | 121 | 125K (1)| 00:25:08 | 
| 2 | VIEW       | VW_DAG_0    |  1 | 121 | 125K (1)| 00:25:08 | 
| 3 | HASH GROUP BY     |      |  1 | 98 | 125K (1)| 00:25:08 | 
| 4 |  NESTED LOOPS     |      |  |  |   |   | 
| 5 |  NESTED LOOPS     |      |  1 | 98 | 125K (1)| 00:25:08 | 
| 6 |  MERGE JOIN CARTESIAN  |      | 12613 | 800K| 21133 (2)| 00:04:14 | 
|* 7 |  TABLE ACCESS BY INDEX ROWID| TABLE1     |  1 | 45 | 46 (0)| 00:00:01 | 
|* 8 |   INDEX RANGE SCAN   | DATA_DATE__STG_BACKUP2 | 1040 |  |  6 (0)| 00:00:01 | 
| 9 |  BUFFER SORT    |      | 182K| 3564K| 21087 (2)| 00:04:14 | 
|* 10 |   TABLE ACCESS FULL   | TABLE3     | 182K| 3564K| 21087 (2)| 00:04:14 | 
|* 11 |  INDEX RANGE SCAN   | BSB_PARTYID_IDX  | 22 |  |  3 (0)| 00:00:01 | 
|* 12 |  TABLE ACCESS BY INDEX ROWID | TABLE2     |  1 | 33 | 10 (0)| 00:00:01 | 
------------------------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    7-filter(SUBSTR(A.ID, 4, 8) = '20150303' AND SUBSTR(A.ID, 1, 2) = 'US' 
       AND A.PROD_TYPE = 'INS') 
    8 - access(A.DUMP_DT = '31-MAR-15') 
    10 - filter(C.ACCT_TYPE = 'LON') 
    11 – access(B.PARTY_ID = C.PARTY_ID) 
    12 - filter(A.ID = B.ID) 
+1

什么'dump_dt'的类型?你有什么指数?什么是解释计划? – Mat

+0

您是否检查过DUMP_DT上有索引? – PhillipD

+2

这对Oracle来说是无效的SQL,它不支持用于别名表的'AS'关键字。有些东西你没有告诉我们......正如其他人所说的DDL表和解释计划是_essential_为了让你得到一个很好的答案,它看起来好像你将日期存储为字符串,这总是一个配方灾难和最后'SUBSTR(ID,4,9)'返回9个字符,而不是8,所以除非ID少于13个字符长度,我期望'SUBSTR(ID,4,9)='20150303'始终返回什么也没有,这意味着你的查询不会返回任何东西你能否澄清你的问题? – Ben

回答

1

貌似优化器是显著低估的行数上TABLE1应用这些4个谓词后返回。

A.PROD_TYPE = 'INS' 
SUBSTR(A.ID, 1, 2) = 'US' 
A.DUMP_DT = '31-MAR-15' 
SUBSTR(ID, 4, 8) = '20150303' 

(略题外话:它的安全使用ANSI文字date '2015-03-31'而不是隐式转换字符串'31-MAR-15'和声明有一些错误,好像缺少了前2个谓语之间的状态和失踪的。 A.在过去的谓语前)

首先,确保有对所有表准确的统计数据,看看是否改变了解释计划:

begin 
    dbms_stats.gather_table_stats(user, 'TABLE1'); 
    dbms_stats.gather_table_stats(user, 'TABLE2'); 
    dbms_stats.gather_table_stats(user, 'TABLE3'); 
end; 
/

的“SMA rt列“,ID,使得很难估计应用条件后返回的行数。如果是来不及更改数据模型,你至少可以为Oracle提供了一些扩展统计信息,以帮助其应对谓词:

select dbms_stats.create_extended_stats(user, 'TABLE1', '(SUBSTR(ID, 1, 2))') from dual; 
select dbms_stats.create_extended_stats(user, 'TABLE1', '(SUBSTR(ID, 4, 8))') from dual; 

我猜测SUBSTR(A.ID, 1, 2) = 'US'是一种流行的价值,但没有扩展统计信息Oracle不会知道这一点。额外的直方图可能会显着增加基数。然后优化器不会选择两个不相关表之间的笛卡尔连接。

-1

尝试使用Oracle提示来选择稳定计划或您可以使用招:

.... 
And A.DUMP_DT+0 = to_date('31-MAR-15','dd-mon- rr') 
... 
1

我在WHERE子句超过A.ID

A.ID LIKE 'US_20150303%' 

简化条件与

substr(a.id, 1, 2) = 'US' AND substr(id, 4, 8) = '20150303' 

相同的效果,并且在情况下列A.ID被索引,应用SUBSTR(a.ID,..)函数的事实使索引无用。

在另一方面,a.dump_dt似乎是一个DATE类型列,所以在此列应用过滤器的优选方式可能是

a.dump_dt = TO_DATE('31-MAR-15', 'DD-MON-RR') 

,而不是

a.dump_dt = '31-MAR-15' 

后者主要依赖于运行查询的Oracle客户端的NLS_DATE_FORMAT,并且在某些情况下,可能会通过忽略对索引的使用而对性能产生负面影响,该索引的使用范围为a.dump_dt

因此改写查询看起来是这样的:

SELECT 
    SUBSTR(A.ID, 1, 2) AS country, 
    COUNT(DISTINCT A.ID) AS id_count, 
    SUM(A.amount)  AS amount 
FROM table1 A 
    JOIN table2 b ON A.ID = b.ID 
    JOIN table3 c ON b.party_id = c.party_id 
WHERE A.prod_type = 'INS' 
    AND c.acct_type = 'LON' 
    AND A.ID LIKE 'US_20150303%' 
    AND A.dump_dt = TO_DATE('31-MAR-15', 'DD-MON-RR') 
GROUP BY SUBSTR(A.ID, 1, 2); 
+0

您好,请问您可以编辑您的答案以解释此查询的作用以及它如何修复所问的问题?只有代码答案是不鼓励的,并且可能会被删除。谢谢。 –