处理数百万行的查询的性能调整
Performance tuning of a query processing millions of rows
我有一个查询处理了 2 table 秒,每条记录超过 6100 万条。
- WB_YH_BCUPDATE_FULL_BASE: table 包含客户及其活跃的所有月份。 (2014年至今)
CUSTOMERNUMBER | CAR MONTH
99999 | 201401
99999 | 201402
99999 | 201403
....
- WB_YH_BCUPDATE_MATCH_MONTH: table 包含客户和所有活跃的月份 + 包含 CAR_MONTH + 6 的虚构额外字段个月。
CUSTOMERNUMBER | CAR_MONTH | MATCH_MONTH_6
99999 | 201401 | 201407
99999 | 201402 | 201408
99999 | 201403 | 201409
...
现在我想检查所有客户及其所有相应的 CAR_MONTHS 是否在 6 个月后仍然活跃(=他们出现在 table 中)。为此,我需要使用我创建的字段,即 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"))
你们知道我如何优化此查询或如何重写此查询以提高性能吗?
亲切的问候,
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);
此查询应该会以更好的性能为您提供所需的结果。
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 个月后活跃,我们将找到一个条目;如果没有,我们不会。
为了完全复制您的查询结果,我们 select 从左侧连接 table 获取 match_month_6 的数据,因为如果我们无法获得也匹配您的原始查询。
您也应该在两个月份字段上放置索引,因为我们也加入了这些字段。
请注意,这并不能保证客户在其间的几个月内处于活跃状态。我有一个客户在 1 月和 7 月活跃,他们将通过此查询返回。
我有一个查询处理了 2 table 秒,每条记录超过 6100 万条。
- WB_YH_BCUPDATE_FULL_BASE: table 包含客户及其活跃的所有月份。 (2014年至今)
CUSTOMERNUMBER | CAR MONTH
99999 | 201401
99999 | 201402
99999 | 201403
....
- WB_YH_BCUPDATE_MATCH_MONTH: table 包含客户和所有活跃的月份 + 包含 CAR_MONTH + 6 的虚构额外字段个月。
CUSTOMERNUMBER | CAR_MONTH | MATCH_MONTH_6
99999 | 201401 | 201407
99999 | 201402 | 201408
99999 | 201403 | 201409
...
现在我想检查所有客户及其所有相应的 CAR_MONTHS 是否在 6 个月后仍然活跃(=他们出现在 table 中)。为此,我需要使用我创建的字段,即 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"))
你们知道我如何优化此查询或如何重写此查询以提高性能吗?
亲切的问候,
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);
此查询应该会以更好的性能为您提供所需的结果。
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 个月后活跃,我们将找到一个条目;如果没有,我们不会。
为了完全复制您的查询结果,我们 select 从左侧连接 table 获取 match_month_6 的数据,因为如果我们无法获得也匹配您的原始查询。
您也应该在两个月份字段上放置索引,因为我们也加入了这些字段。
请注意,这并不能保证客户在其间的几个月内处于活跃状态。我有一个客户在 1 月和 7 月活跃,他们将通过此查询返回。