优化SELECT查询性能

时间:2016-07-27 12:33:25

标签: sql oracle performance optimization view

我有一个SELECT语句运行得非常慢,它阻碍了我们的夜间进程。

查询是:(请不要评论隐式连接语法,这是由运行此代码的Informatica自动生成的):

SELECT *
  FROM STG_DIM_CRM_CASES,V_CRM_CASE_ID_EXISTS_IN_DWH,stg_scd_customers_key
 WHERE STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+)
   AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+)
   and STG_DIM_CRM_CASES.Case_Create_Date between  stg_scd_customers_key.start_date(+) and  stg_scd_customers_key.end_date(+)

编辑:实际查询仅选择account_number,start_date,end_date和另一个未编制索引的列。

表信息:

STG_DIM_CRM_CASES

Index - (Account_Number,Case_Create_Date)
size - 270k records.

stg_scd_customers_key

Index - Account_Number,Start_Date,End_Date
Partitioned - End_Date
Size - 500 million records.

V_CRM_CASE_ID_EXISTS_IN_DWH(查看) -

select  t.case_id
from crm_ps_rc_case t, dim_crm_cases x
where t.case_id=x.crm_case_id;

dim_crm_cases -

Indexed - (crm_case_id)
Size - 100 million .

crm_ps_rc_case -

Size - 270k records

修改 - 如果不清楚,视图会返回270k记录。

没有连接到stg_scd的查询需要几秒钟,看起来它是导致性能问题的部分,视图也在几秒钟内运行,尽管它正在加入到1亿个记录表中。现在查询占用的时间在12到30分钟之间,取决于我们的来源有多繁忙。

这是执行计划:

6   |   0 | SELECT STATEMENT                |                             |  3278K|  1297M|   559K  (4)| 02:10:37 |       |       |        |      |            |
7   |   1 |  PX COORDINATOR                 |                             |       |       |            |          |       |       |        |      |            |
8   |   2 |   PX SEND QC (RANDOM)           | :TQ10003                    |  3278K|  1297M|   559K  (4)| 02:10:37 |       |       |  Q1,03 | P->S | QC (RAND)  |
9   |*  3 |    HASH JOIN OUTER              |                             |  3278K|  1297M|   559K  (4)| 02:10:37 |       |       |  Q1,03 | PCWP |            |
10  |   4 |     PX RECEIVE                  |                             | 29188 |    10M| 50662   (5)| 00:11:50 |       |       |  Q1,03 | PCWP |            |
11  |   5 |      PX SEND HASH               | :TQ10002                    | 29188 |    10M| 50662   (5)| 00:11:50 |       |       |  Q1,02 | P->P | HASH       |
12  |*  6 |       HASH JOIN RIGHT OUTER     |                             | 29188 |    10M| 50662   (5)| 00:11:50 |       |       |  Q1,02 | PCWP |            |
13  |   7 |        BUFFER SORT              |                             |       |       |            |          |       |       |  Q1,02 | PCWC |            |
14  |   8 |         PX RECEIVE              |                             | 29188 |   370K| 50575   (5)| 00:11:49 |       |       |  Q1,02 | PCWP |            |
15  |   9 |          PX SEND BROADCAST      | :TQ10000                    | 29188 |   370K| 50575   (5)| 00:11:49 |       |       |        | S->P | BROADCAST  |
16  |  10 |           VIEW                  | V_CRM_CASE_ID_EXISTS_IN_DWH | 29188 |   370K| 50575   (5)| 00:11:49 |       |       |        |      |            |
17  |* 11 |            HASH JOIN            |                             | 29188 |   399K| 50575   (5)| 00:11:49 |       |       |        |      |            |
18  |  12 |             TABLE ACCESS FULL   | CRM_PS_RC_CASE              | 29188 |   199K|   570   (1)| 00:00:08 |       |       |        |      |            |
19  |  13 |             INDEX FAST FULL SCAN| DIM_CRM_CASES$1PK           |   103M|   692M| 48894   (3)| 00:11:25 |       |       |        |      |            |
20  |  14 |        PX BLOCK ITERATOR        |                             | 29188 |    10M|    87   (2)| 00:00:02 |       |       |  Q1,02 | PCWC |            |
21  |  15 |         TABLE ACCESS FULL       | STG_DIM_CRM_CASES           | 29188 |    10M|    87   (2)| 00:00:02 |       |       |  Q1,02 | PCWP |            |
22  |  16 |     BUFFER SORT                 |                             |       |       |            |          |       |       |  Q1,03 | PCWC |            |
23  |  17 |      PX RECEIVE                 |                             |   515M|    14G|   507K  (3)| 01:58:28 |       |       |  Q1,03 | PCWP |            |
24  |  18 |       PX SEND HASH              | :TQ10001                    |   515M|    14G|   507K  (3)| 01:58:28 |       |       |        | S->P | HASH       |
25  |  19 |        PARTITION RANGE ALL      |                             |   515M|    14G|   507K  (3)| 01:58:28 |     1 |  2982 |        |      |            |
26  |  20 |         TABLE ACCESS FULL       | STG_SCD_CUSTOMERS_KEY       |   515M|    14G|   507K  (3)| 01:58:28 |     1 |  2982 |        |      |            |
27  ------------------------------------------------------------------------------------------------------------------------------------------------------------
28   
29  Predicate Information (identified by operation id):
30  ---------------------------------------------------
31   
32     3 - access("STG_DIM_CRM_CASES"."ACCOUNT_NUMBER"="STG_SCD_CUSTOMERS_KEY"."ACCOUNT_NUMBER"(+))
33         filter("STG_DIM_CRM_CASES"."CASE_CREATE_DATE">="STG_SCD_CUSTOMERS_KEY"."START_DATE"(+) AND 
34                "STG_DIM_CRM_CASES"."CASE_CREATE_DATE"<="STG_SCD_CUSTOMERS_KEY"."END_DATE"(+))
35     6 - access("STG_DIM_CRM_CASES"."CRM_CASE_ID"="V_CRM_CASE_ID_EXISTS_IN_DWH"."CASE_ID"(+))
36    11 - access("T"."CASE_ID"="X"."CRM_CASE_ID")

注意:添加索引可能是一个问题,取决于索引。这不是这个表被使用的唯一地方,因此索引可能会干扰这些表上的其他命令(主要是Insert)。

我还尝试在stg_scd上添加过滤器,并排除小于Table_Cases中最小日期的所有日期,但这没有任何帮助,因为它只过滤了1年的记录。< / p>

提前致谢。

6 个答案:

答案 0 :(得分:3)

<强>理论值: 我认为发生的事情是引擎在应用限制标准之前必须从视图连接解析100m +记录到500m记录(因此它创建了一个交叉连接,即使它可以使用多个记录的索引到生成然后解析。所以即使你把它写成外部联接,引擎也不能以那种方式处理它(我不知道为什么)

所以至少100m * 500m = 50,000m需要生成大量数据然后解析/限制。

通过消除视图,引擎可以更好地优化和使用索引,从而无需50,000m记录连接。

我将把时间集中在故障排除方面的领域:

  • 删除视图只是为了将其删除作为潜在的开销问题。
  • 认识到stg_scd_customers_key和V_CRM_CASE_ID_EXISTS_IN_DWH之间没有关联。这意味着引擎可能在结果之前进行交叉连接 STG_DIM_CRM_CASES到stg_scd_customers_key已经解决。

考虑删除视图或使用内嵌视图

取消观点:

SELECT *
  FROM STG_DIM_CRM_CASES 
      ,crm_ps_rc_case t
      ,dim_crm_cases x 
      ,stg_scd_customers_key
 WHERE t.case_id=x.crm_case_id
   AND STG_DIM_CRM_CASES.CRM_CASE_ID = t.CASE_ID(+)
   AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+)
   AND STG_DIM_CRM_CASES.Case_Create_Date 
       between  stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+)

使用内联视图:

SELECT *
  FROM STG_DIM_CRM_CASES 
  (select  t.case_id
   from crm_ps_rc_case t, dim_crm_cases x
   where t.case_id=x.crm_case_id) V_CRM_CASE_ID_EXISTS_IN_DWH
      ,stg_scd_customers_key
 WHERE STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+)
   AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+)
   AND STG_DIM_CRM_CASES.Case_Create_Date 
       between  stg_scd_customers_key.start_date(+) and stg_scd_customers_key.end_date(+)

至于原因:   - http://www.dba-oracle.com/art_hints_views.htm

虽然where子句的顺序应该考虑:在关闭追踪中,引擎按所列顺序执行,向下限制500米,然后从视图中添加补充数据在逻辑上会更快。

SELECT *
  FROM STG_DIM_CRM_CASES,stg_scd_customers_key,V_CRM_CASE_ID_EXISTS_IN_DWH
 WHERE STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+)
   and STG_DIM_CRM_CASES.Case_Create_Date between  stg_scd_customers_key.start_date(+) and  stg_scd_customers_key.end_date(+)
   and STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+)

答案 1 :(得分:1)

问题在于扫描所有分区:

  

18 | PX SEND HASH | :TQ10001 |   515M | 14G | 507K(3)| 01:58:28 | | | | S-> P |   哈希| 25 | 19 |分区范围全部|
  | 515M | 14G | 507K(3)| 01:58:28 | 1 | 2982 | |
  | | 26 | 20 |表访问完全|   STG_SCD_CUSTOMERS_KEY | 515M | 14G |

之所以发生这种情况,是因为您正在使用左连接到此表。你能用bind变量选择1个分区吗?什么是分区键? 我没有看到提示并行,但根据你的计划它使用并行。在任何对象级别上是否存在平行度?您可以在不平行的情况下删除并行和后期解释计划吗?

答案 2 :(得分:1)

我认为问题是视图,我怀疑它正在执行并在条件应用之前返回所有行

视图的整体效果是添加列CASE_ID,如果在其中找到CRM_CASE_ID则不为空,否则为null。我用两个直接连接和一个CASE表达式替换了视图。通过用逻辑替换视图的便利性,您可以直接连接到其中的每个表,从而避免一个级别的连接深度。

尝试运行此版本的查询:

SELECT
  a.*, b.*, c.*,
  CASE WHEN t.case_id is not null and X.case_id is not null then t.case_id END CASE_ID
FROM STG_DIM_CRM_CASES a
LEFT JOIN crm_ps_rc_case t
  ON t.case_id = a.CRM_CASE_ID
LEFT JOIN dim_crm_cases x
  ON x.crm_case_id = a.CRM_CASE_ID
LEFT JOIN V_CRM_CASE_ID_EXISTS_IN_DWH b
  ON a.CRM_CASE_ID = b.CASE_ID
LEFT JOIN stg_scd_customers_key c
  ON a.account_number = c.account_number
 and a.Case_Create_Date between c.start_date and  stg_scd_customers_key.end_date

如果将a.*, b.*, c.*替换为您实际需要的确切列,则会加快速度,因为返回的数据更少。如果您还将索引放在查找键和实际选择的所有列(覆盖索引)上,您将大大加快速度,因为可以使用仅索引访问。

您应该至少验证所有已加入列中的索引。

答案 3 :(得分:1)

您的问题是Oracle实际上只有两种方法可以从stg_scd_customers_key获取所需的行。要么(A)它执行该表的单个FULL SCAN然后过滤掉它不想要的行,否则(B)它执行270,000个索引查找,每个3到5个逻辑I / O(取决于在索引的高度上),加上另外1个逻辑I / O来实际从表中读取块。

考虑到FULL SCAN可用的多块读取和其他优化,并且基于您的表统计信息,Oracle的优化器猜测FULL SCAN会更快。而且很有可能它是正确的。

您需要做的是为Oracle提供更好的选择。

如果你不能在任何地方使用物化视图,那么一个好的“穷人”物化视图就是所谓的覆盖索引。现在,这对您的查询来说是不合理的,因为您执行了SELECT *。但你真的需要stg_scd_customers_key的每一栏吗?

如果您可以减少从stg_scd_customers_key获得的列的列表,则可以创建一个(A)以account_numberstart_dateend_date开头的索引(B)包括您需要选择的所有其他列。

例如:

SELECT stg_im_crm_cases.*, V_CRM_CASE_ID_EXISTS_IN_DWH.*, stg_scd_customers_key.column_1, stg_scd_customers_key.column_2
  FROM STG_DIM_CRM_CASES,V_CRM_CASE_ID_EXISTS_IN_DWH,stg_scd_customers_key
 WHERE STG_DIM_CRM_CASES.CRM_CASE_ID = V_CRM_CASE_ID_EXISTS_IN_DWH.CASE_ID(+)
   AND STG_DIM_CRM_CASES.account_number = stg_scd_customers_key.account_number(+)
   and STG_DIM_CRM_CASES.Case_Create_Date between  stg_scd_customers_key.start_date(+) and  stg_scd_customers_key.end_date(+)

如果您可以进行查询,并在stg_scd_customers_key(account_number,start_date,end_date,column_1,column_2)上创建索引,那么您将为Oracle提供更好的选择。现在它可以单独读取索引,而不是表。

对于大表,在你尝试之前没有任何保证。但覆盖指数往往就是医生所要求的。 (关于新索引的所有常见警告当然都适用)。

答案 4 :(得分:1)

一些注意事项:

1)INDEX
crm_ps_rc_case case_id上没有索引这是一个问题,您加入了270k&lt; - &gt; 100米与HASH JOIN(不好)

2)SELECTED COLUMNS
视图V_CRM_CASE_ID_EXISTS_IN_DWH会选择t.case_id,但至少应选择x.crm_case_id,直到您不解析t.case_id的索引为止。这会在你的所有执行计划中传播HASH JOIN ..(不好)

3)BETWEEN
范围连接/过滤始终是一个问题,尤其是在大型表上,但您可以限制问题在范围上添加条件。让我解释一下,尝试将这些条件添加到您的WHERE子句中:

AND stg_scd_customers_key.end_date =  (
       SELECT min(r.end_date)
       FROM stg_scd_customers_key r
       WHERE  r.end_date >= STG_DIM_CRM_CASES.Case_Create_Date
)
AND stg_scd_customers_key.start_date =  (
       SELECT max(r.start_date)
       FROM stg_scd_customers_key r
       WHERE  r.start_date <= STG_DIM_CRM_CASES.Case_Create_Date
)

是的,它将计算270k * 2子查询,但最终的连接将在更少的recs限制IO操作上工作(它应该更好)

4)INDEX COLUMN ORDER
如果确实存在相互矛盾的报道,或者它无关紧要,但根据我的经验......确实如此。 这可能只是一个小改进,但您可以尝试修改stg_scd_customers_key上的索引,反转Start_DateEnd_Date的顺序,根据我的经验,我发现它对范围过滤更有效在索引的下限之前有上限。

答案 5 :(得分:-1)

我会考虑为refresh fast on demandTable_cases之间的联接创建一个实体化视图(stg_scd)。我假设联接中的大部分工作都是在每天都不会改变的行上。