处理数百万行的查询的性能调优

时间:2016-11-18 20:57:45

标签: sql oracle query-performance

我有一个查询处理2个表,每个表有超过6100万条记录。

  • WB_YH_BCUPDATE_FULL_BASE:表包含客户及其活跃的所有月份。 (从2014年至今)
  

CUSTOMERNUMBER | CAR MONTH

           

99999 | 201401个
  99999 | 201402个
  99999 | 201403个
  ....

  • WB_YH_BCUPDATE_MATCH_MONTH:表包含客户以及所有活跃的月份+一个包含CAR_MONTH + 6个月的虚拟额外字段。
  

CUSTOMERNUMBER | CAR_MONTH | MATCH_MONTH_6

           

99999 | 201401 | 201407个
  99999 | 201402 | 201408个
  99999 | 201403 | 201409个
  ...

现在我想检查所有客户及其所有相应的CAR_MONTHS,如果它们在6个月后仍处于活动状态(=它们出现在表格中)。为此,我需要使用我创建的字段,即MATCH_MONTH_6。

我使用以下查询:

select distinct a.CUSTOMERNUMBER
   , a.CAR_MONTH
   , b.MATCH_MONTH_6
   , CASE WHEN b.CUSTOMERNUMBER is null then 0
          ELSE 1
     END FL_MATCH_6         
from WB_YH_BCUPDATE_FULL_BASE a left join WB_YH_BCUPDATE_MATCH_MONTH b
                                on a.CUSTOMERNUMBER =  b.CUSTOMERNUMBER      
                                and a.CAR_MONTH = b.CAR_MONTH 
                                and b.MATCH_MONTH_6 in (
                                select CAR_MONTH
                                from   WB_YH_BCUPDATE_FULL_BASE
                                where  customernumber = a.customernumber
                                );

我的查询表现非常糟糕,您可以从以下执行计划中看到:

 Plan Hash Value  : 3376431373 

-----------------------------------------------------------------------------------------------------------------------------
| Id   | Operation                          | Name                           | Rows     | Bytes      | Cost      | Time     |
-----------------------------------------------------------------------------------------------------------------------------
|    0 | SELECT STATEMENT                   |                                | 25897713 |  673340538 | 371846479 | 02:56:04 |
|    1 |   HASH UNIQUE                      |                                | 25897713 |  673340538 | 371846479 | 02:56:04 |
|    2 |    NESTED LOOPS OUTER              |                                | 61874441 | 1608735466 | 371674345 | 02:55:59 |
|    3 |     TABLE ACCESS STORAGE FULL      | WB_YH_BCUPDATE_FULL_BASE       | 61874441 |  742493292 |      3225 | 00:00:01 |
|    4 |     VIEW                           |                                |        1 |         14 |         6 | 00:00:01 |
|    5 |      NESTED LOOPS                  |                                |        1 |         31 |         6 | 00:00:01 |
|    6 |       NESTED LOOPS                 |                                |       24 |         31 |         6 | 00:00:01 |
|  * 7 |        TABLE ACCESS BY INDEX ROWID | WB_YH_BCUPDATE_MATCH_MONTH     |        1 |         19 |         3 | 00:00:01 |
|  * 8 |         INDEX RANGE SCAN           | WB_YH_BCUPDATE_MATCH_MONTH_IND |       24 |            |         2 | 00:00:01 |
|  * 9 |        INDEX RANGE SCAN            | WB_YH_BCUPDATE_FULL_BASE_IND   |       24 |            |         2 | 00:00:01 |
| * 10 |       TABLE ACCESS BY INDEX ROWID  | WB_YH_BCUPDATE_FULL_BASE       |        1 |         12 |         3 | 00:00:01 |
-----------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 7 - filter("A"."CAR_MONTH"="B"."CAR_MONTH")
* 8 - access("A"."CUSTOMERNUMBER"="B"."CUSTOMERNUMBER")
* 9 - access("CUSTOMERNUMBER"="A"."CUSTOMERNUMBER")
* 10 - filter("CAR_MONTH"=TO_NUMBER("B"."MATCH_MONTH_6"))

你们对我如何优化这个查询或者如何重写这个查询以获得更高的性能有任何想法吗?

亲切的问候,

2 个答案:

答案 0 :(得分:2)

SELECT
   a.customernumber,
   a.car_month,
   b.car_month AS match_month_6,
   CASE
      WHEN b.customernumber IS NULL
      THEN 0
      END 1
   END AS fl_match_6
FROM WB_YH_BCUPDATE_MATCH_MONTH a
LEFT JOIN WB_YH_BCUPDATE_MATCH_MONTH b
   ON (a.customernumber = b.Customernumber AND a.match_month_6 = b.car_month);

由于您说WB_YH_BCUPDATE_MATCH_MONTH包含的数据与WB_YH_BCUPDATE_FULL_BASE相同,但只有一列,我们可以使用前者而忽略后者。

我们现在离开加入它自己。当然关于客户编号,我们也加入了日期+ 6个月的日期。如果客户在6个月后处于活动状态,我们会找到一个条目;如果没有,我们就赢了。

要完全复制查询结果,我们选择从左连接表中获取match_month_6的数据,因为如果我们在原始查询中也无法获得匹配,则为NULL。

您也应该在两个月份字段中放置索引,因为我们也加入了这些索引。

请注意,这并不能保证客户在两个月之间保持活跃状态​​。我的客户在1月份和7月份都处于活动状态,此查询将返回这些客户。

答案 1 :(得分:0)

select w1.CUSTOMERNUMBER, w1.CAR_MONTH, nvl2(w2.CUSTOMERNUMBER, 'Yes', 'No') active_in_6_months
  from WB_YH_BCUPDATE_FULL_BASE w1 
  left outer join WB_YH_BCUPDATE_MATCH_MONTH w2 
  on (w1.CUSTOMERNUMBER = w2. CUSTOMERNUMBER and w1.CAR_MONTH = w2.MATCH_MONTH_6);

此查询应该可以为您提供更好的效果。