Sql Oracle-查询运行非常慢

Sql Oracle-查询运行非常慢,sql,oracle,Sql,Oracle,我有一个永远运行的简单查询。有一个日期条件,一旦我删除它,查询就会返回结果。它是一个格式为“2015年3月31日”的日期字段。我不明白为什么这种情况使得查询速度如此之慢。提前谢谢 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 JO

我有一个永远运行的简单查询。有一个日期条件,一旦我删除它,查询就会返回结果。它是一个格式为“2015年3月31日”的日期字段。我不明白为什么这种情况使得查询速度如此之慢。提前谢谢

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
上应用这4个谓词后,优化器似乎大大低估了返回的行数

A.PROD_TYPE = 'INS'
SUBSTR(A.ID, 1, 2) = 'US'
A.DUMP_DT = '31-MAR-15'
SUBSTR(ID, 4, 8) = '20150303'
(稍微偏离主题:更安全的做法是使用ANSI文字
日期'2015-03-31'
而不是隐式转换的字符串
'31-MAR-15'
。并且该语句有一些错误,例如前两个谓词之间缺少条件,最后一个谓词前面缺少
a.
。)

首先,确保所有表格都有准确的统计数据,看看这是否会改变解释计划:

begin
    dbms_stats.gather_table_stats(user, 'TABLE1');
    dbms_stats.gather_table_stats(user, 'TABLE2');
    dbms_stats.gather_table_stats(user, 'TABLE3');
end;
/
“智能列”
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就不会知道这一点。额外的直方图可能会显著增加基数。这样优化器就不会在两个不相关的表之间选择笛卡尔连接。

尝试使用oracle提示来稳定选择计划,或者您可以使用该技巧:

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

我简化了A.ID字段上WHERE子句的条件

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似乎是一个日期类型的列,因此对该列应用筛选器的首选方法可能是

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

a.dump_dt = '31-MAR-15'
后者主要取决于运行查询的Oracle客户端的NLS_DATE_格式,在某些情况下,通过忽略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);

转储的类型是什么?你有什么索引?解释计划是什么?您是否检查了DUMP_DT上是否有索引?这对于Oracle是无效的SQL,它不支持将
作为别名表的关键字。有些事你没告诉我们。。。正如其他人所说,为了得到一个好的答案,表DDL和explain plans是必不可少的,它似乎是将日期存储为字符串,这总是导致灾难,最后
SUBSTR(ID,4,9)
返回9个字符,而不是8个,所以除非ID长度小于13个字符,否则我希望
SUBSTR(ID,4,9)='20150303'
始终不返回任何内容,这意味着您的查询不会返回任何内容。你能澄清一下你的问题吗?dump_dt确实有一个索引,请在你的问题中添加该索引、相关索引和解释计划。(并且在发布此类问题时始终从一开始就添加该信息。)您好,请编辑您的答案,解释此查询的作用以及它如何解决所问问题?不鼓励只使用代码的答案,这些答案可能会被删除。谢谢