我有一个包含 3600 万条记录的表,我需要在某些条件下更新字段,但每个查询需要 10 分钟,而且我有很多查询要运行:
MERGE INTO account USING ( SELECT id, COUNT( Account) as numAcc
FROM account where Status = 'ACTIVATED' GROUP BY id) ba
ON (ba.id = b.id ) WHEN MATCHED THEN UPDATE SET b.numAcc = ba.numAcc;
上面的示例查询和其他查询看起来相同,但条件发生了变化,以下是解释
-------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 36M| 892M| | 610K (1)| 00:00:24 |
| 1 | MERGE | ACCOUNT | | | | | |
| 2 | VIEW | | | | | | |
|* 3 | HASH JOIN | | 36M| 19G| 511M| 610K (1)| 00:00:24 |
| 4 | VIEW | | 998K| 500M| | 365K (1)| 00:00:15 |
| 5 | SORT GROUP BY | | 998K| 502M| | 365K (1)| 00:00:15 |
| 6 | VIEW | VW_DAG_1 | 25M| 12G| | 365K (1)| 00:00:15 |
| 7 | SORT GROUP BY | | 25M| 1025M| 1346M| 365K (1)| 00:00:15 |
|* 8 | TABLE ACCESS FULL| ACCOUNT | 25M| 1025M| | 91408 (1)| 00:00:04 |
| 9 | TABLE ACCESS FULL | ACCOUNT | 36M| 2162M| | 91501 (1)| 00:00:04 |
-------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("BA"."ID"="B"."ID")
8 - filter("Status "='ACTIVATED')
您可以尝试使用分析函数并在
id
伪列(实际上是指向行的指针)上进行关联,而不是在 ROWID
列上进行聚合和关联:
MERGE INTO account dst
USING (
SELECT COUNT(CASE WHEN Status = 'ACTIVATE' THEN account END)
OVER (PARTITION BY id) AS activated_count
FROM account
) src
ON (
dst.ROWID = src.ROWID
AND src.activated_count > 0 -- Your query effectively includes this filter but I'm not sure
-- whether you intended that behaviour or not.
)
WHEN MATCHED THEN
UPDATE SET dst.numAcc = src.activated_count;
您还可以检查该列是否已更改,如果没有更改,请跳过更新:
MERGE INTO account dst
USING (
SELECT COUNT(CASE WHEN Status = 'ACTIVATE' THEN account END)
OVER (PARTITION BY id) AS activated_count
FROM account
) src
ON (
dst.ROWID = src.ROWID
AND src.activated_count > 0 -- Your query effectively includes this filter but I'm not sure
-- whether you intended that behaviour or not.
)
WHEN MATCHED THEN
UPDATE
SET dst.numAcc = src.activated_count
WHERE dst.numAcc != src.activated_count;