关于优化在大量数据上执行的 Oracle SQL 查询的建议/技巧
Posted
技术标签:
【中文标题】关于优化在大量数据上执行的 Oracle SQL 查询的建议/技巧【英文标题】:Advices/tips on optimizing an Oracle SQL query executing on significant volumes 【发布时间】:2013-03-13 16:36:10 【问题描述】:不要在看到下面的 Oracle SQL 查询后立即运行! :)
我将完整的查询放在 orderrer 中,以征求一些关于优化的建议。 我使用 Oracle 解释计划工具来帮助我确定一些优化方法,但我非常坚持。 你能给我一些关于这个查询的潜在优化的建议/提示/好的做法吗?
事实:
PERSONNE 和 AFFAIRE 表包含大约 100 万行 表 EVENEMENT 包含大约 3000 万行提前致谢!
SELECT COUNT(*)
FROM(
(SELECT DISTINCT PER_ID
FROM (
SELECT evt.PER_ID, MAX(evt.EVT_DATE_UPDATE) DMAX
FROM EVENEMENT evt, PERSONNE per
WHERE per.PER_ID = evt.PER_ID
AND (per.PER_SI_DECES <> 1)
GROUP BY evt.PER_ID) dos
WHERE NOT EXISTS (
SELECT 1
FROM AFFAIRE aff, PRISE_EN_CHARGE pec
WHERE dos.PER_ID = aff.PER_ID
AND aff.AFF_ID = pec.AFF_ID
AND pec.PEC_ETAT = 'A')
AND DMAX < ADD_MONTHS(SYSDATE,-60)
)
UNION
(SELECT per.PER_ID
FROM PERSONNE per
WHERE per.PER_SI_DECES = 1
AND NOT EXISTS (
SELECT 1
FROM AFFAIRE aff, PRISE_EN_CHARGE pec
WHERE per.PER_ID = aff.PER_ID
AND aff.AFF_ID = pec.AFF_ID
AND pec.PEC_ETAT = 'A')
AND EXISTS (
SELECT 1
FROM AFFAIRE aff
WHERE per.PER_ID = aff.PER_ID)
)
)
WHERE PER_ID NOT IN (
(SELECT pdo.PER_ID
FROM PERSONNE_DOSSIER pdo, EVENEMENT evt
WHERE pdo.ROP_ID = 1
AND evt.PDO_ID = pdo.PDO_ID
AND evt.PER_ID <> pdo.PER_ID
)
UNION
(SELECT pdo.PER_ID
FROM PERSONNE_DOSSIER pdo, DESTINATAIRE_EVENEMENT des, EVENEMENT evt
WHERE pdo.ROP_ID = 1
AND des.PDO_ID = pdo.PDO_ID
AND des.EVT_ID = evt.EVT_ID
AND evt.PER_ID <> pdo.PER_ID)
UNION
(SELECT dgi.PER_ID
FROM DEMANDE_GIDE dgi, AFFAIRE aff
where aff.AFF_ID = dgi.AFF_ID
and aff.PER_ID <> dgi.PER_ID)
);
下面是相应的解释计划:
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | | 194K (1)|
| 1 | SORT AGGREGATE | | 1 | 13 | | |
|* 2 | FILTER | | | | | |
| 3 | VIEW | | 1309 | 17017 | | 192K (1)|
| 4 | SORT UNIQUE | | 1309 | 35344 | | 192K (24)|
| 5 | UNION-ALL | | | | | |
|* 6 | FILTER | | | | | |
| 7 | HASH GROUP BY | | 1 | 28 | | 149K (1)|
|* 8 | HASH JOIN RIGHT ANTI | | 1021K| 27M| 12M| 149K (1)|
| 9 | VIEW | VW_SQ_1 | 756K| 4433K| | 26286 (1)|
|* 10 | HASH JOIN | | 756K| 14M| 14M| 26286 (1)|
|* 11 | INDEX FAST FULL SCAN | IDX_PEC_AFF_SER_ETAT | 756K| 5910K| | 4080 (3)|
| 12 | TABLE ACCESS FULL | AFFAIRE | 3146K| 36M| | 17884 (1)|
|* 13 | HASH JOIN | | 3648K| 76M| 2560K| 115K (1)|
|* 14 | TABLE ACCESS FULL | PERSONNE | 130K| 1021K| | 14179 (2)|
| 15 | TABLE ACCESS FULL | EVENEMENT | 29M| 391M| | 65035 (2)|
|* 16 | HASH JOIN SEMI | | 1308 | 35316 | | 43080 (1)|
|* 17 | HASH JOIN ANTI | | 1308 | 27468 | 2560K| 41493 (1)|
|* 18 | TABLE ACCESS FULL | PERSONNE | 130K| 1021K| | 14179 (2)|
| 19 | VIEW | VW_SQ_2 | 756K| 9605K| | 26286 (1)|
|* 20 | HASH JOIN | | 756K| 14M| 14M| 26286 (1)|
|* 21 | INDEX FAST FULL SCAN | IDX_PEC_AFF_SER_ETAT | 756K| 5910K| | 4080 (3)|
| 22 | TABLE ACCESS FULL | AFFAIRE | 3146K| 36M| | 17884 (1)|
| 23 | INDEX FAST FULL SCAN | FK_PER_AFF | 3146K| 18M| | 1572 (2)|
| 24 | SORT UNIQUE | | 6 | 171 | | 1757 (100)|
| 25 | UNION-ALL | | | | | |
| 26 | NESTED LOOPS | | | | | |
| 27 | NESTED LOOPS | | 2 | 46 | | 8 (0)|
|* 28 | TABLE ACCESS BY INDEX ROWID | PERSONNE_DOSSIER | 1 | 15 | | 4 (0)|
|* 29 | INDEX RANGE SCAN | FK_PER_PDO | 1 | | | 3 (0)|
|* 30 | INDEX RANGE SCAN | FK_EVT_PDO | 2 | | | 2 (0)|
|* 31 | TABLE ACCESS BY INDEX ROWID | EVENEMENT | 2 | 16 | | 4 (0)|
| 32 | NESTED LOOPS | | | | | |
| 33 | NESTED LOOPS | | 3 | 108 | | 16 (0)|
| 34 | NESTED LOOPS | | 3 | 72 | | 10 (0)|
|* 35 | TABLE ACCESS BY INDEX ROWID| PERSONNE_DOSSIER | 1 | 15 | | 4 (0)|
|* 36 | INDEX RANGE SCAN | FK_PER_PDO | 1 | | | 3 (0)|
| 37 | TABLE ACCESS BY INDEX ROWID| DESTINATAIRE_EVENEMENT | 3 | 27 | | 6 (0)|
|* 38 | INDEX RANGE SCAN | FK_DVT_PDO | 3 | | | 2 (0)|
|* 39 | INDEX UNIQUE SCAN | PK_EVENEMENT | 1 | | | 1 (0)|
|* 40 | TABLE ACCESS BY INDEX ROWID | EVENEMENT | 1 | 12 | | 2 (0)|
| 41 | NESTED LOOPS | | | | | |
| 42 | NESTED LOOPS | | 1 | 17 | | 1730 (2)|
|* 43 | TABLE ACCESS FULL | DEMANDE_GIDE | 1 | 5 | | 1728 (2)|
|* 44 | INDEX UNIQUE SCAN | PK_AFFAIRE | 1 | | | 1 (0)|
|* 45 | TABLE ACCESS BY INDEX ROWID | AFFAIRE | 1 | 12 | | 2 (0)|
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter( NOT EXISTS ( (SELECT "PDO"."PER_ID" FROM "EVENEMENT" "EVT","PERSONNE_DOSSIER"
"PDO" WHERE "PDO"."PER_ID"=:B1 AND "PDO"."ROP_ID"=1 AND "EVT"."PDO_ID"="PDO"."PDO_ID" AND
"EVT"."PER_ID"<>:B2 AND "EVT"."PER_ID"<>"PDO"."PER_ID" AND "EVT"."PDO_ID" IS NOT NULL)UNION
(SELECT "PDO"."PER_ID" FROM "EVENEMENT" "EVT","DESTINATAIRE_EVENEMENT" "DES","PERSONNE_DOSSIER"
"PDO" WHERE "PDO"."PER_ID"=:B3 AND "PDO"."ROP_ID"=1 AND "DES"."PDO_ID"="PDO"."PDO_ID" AND
"DES"."PDO_ID" IS NOT NULL AND "DES"."EVT_ID"="EVT"."EVT_ID" AND "EVT"."PER_ID"<>:B4 AND
"EVT"."PER_ID"<>"PDO"."PER_ID")UNION (SELECT "DGI"."PER_ID" FROM "AFFAIRE" "AFF","DEMANDE_GIDE"
"DGI" WHERE "DGI"."AFF_ID" IS NOT NULL AND "DGI"."PER_ID"=:B5 AND "AFF"."AFF_ID"="DGI"."AFF_ID"
AND "AFF"."PER_ID"<>:B6 AND "AFF"."PER_ID"<>"DGI"."PER_ID")))
6 - filter(MAX("EVT"."EVT_DATE_UPDATE")<ADD_MONTHS(SYSDATE@!,-60))
8 - access("EVT"."PER_ID"="ITEM_1")
10 - access("AFF"."AFF_ID"="PEC"."AFF_ID")
11 - filter("PEC"."PEC_ETAT"='A')
13 - access("PER"."PER_ID"="EVT"."PER_ID")
14 - filter("PER"."PER_SI_DECES"<>1)
16 - access("PER"."PER_ID"="AFF"."PER_ID")
17 - access("PER"."PER_ID"="ITEM_2")
18 - filter("PER"."PER_SI_DECES"=1)
20 - access("AFF"."AFF_ID"="PEC"."AFF_ID")
21 - filter("PEC"."PEC_ETAT"='A')
28 - filter("PDO"."ROP_ID"=1)
29 - access("PDO"."PER_ID"=:B1)
30 - access("EVT"."PDO_ID"="PDO"."PDO_ID")
filter("EVT"."PDO_ID" IS NOT NULL)
31 - filter("EVT"."PER_ID"<>:B1 AND "EVT"."PER_ID"<>"PDO"."PER_ID")
35 - filter("PDO"."ROP_ID"=1)
36 - access("PDO"."PER_ID"=:B1)
38 - access("DES"."PDO_ID"="PDO"."PDO_ID")
filter("DES"."PDO_ID" IS NOT NULL)
39 - access("DES"."EVT_ID"="EVT"."EVT_ID")
40 - filter("EVT"."PER_ID"<>:B1 AND "EVT"."PER_ID"<>"PDO"."PER_ID")
43 - filter("DGI"."AFF_ID" IS NOT NULL AND "DGI"."PER_ID"=:B1)
44 - access("AFF"."AFF_ID"="DGI"."AFF_ID")
45 - filter("AFF"."PER_ID"<>:B1 AND "AFF"."PER_ID"<>"DGI"."PER_ID")
【问题讨论】:
【参考方案1】:这里有很多可以说和问的事情。当 IS_DECES = 0 vs =1 等条件互斥时,使用 UNION ALL 而不是 UNION。 NOT IN 确保您没有使用 NULLable 列。尝试在 NOT IN 中检查 UNION 中的分隔部分,以便为索引访问提供更好的机会。并行查询在这里应该可以帮助您...
【讨论】:
以上是关于关于优化在大量数据上执行的 Oracle SQL 查询的建议/技巧的主要内容,如果未能解决你的问题,请参考以下文章