Недавно я столкнулся со странностью в поведении Oracle12c (12.2.0.1) с или -условными левыми объединениями.
У нас есть две схематически идентичные базы данных с небольшими различиями в данных, одна из которых является базой данных среды тестирования, а другая - производственной базой данных. Параметры оптимизатора одинаковы в обеих базах данных.
Учитывая запрос:
SELECT A.Id as ObjectID,A.GlobalId as GlobalId,A.Type as Type,
(SELECT listagg(CLASS, ',') within group (order by CLASS) from D_VIEW D_SEPARATE_QUERY
where D_SEPARATE_QUERY.A_ID = A.OBJECTID) as D_CLASS_LIST,
Max(CASE WHEN (D.REASON = 1) Then 1 Else 0 END) as CONDITION_1,
Max(CASE WHEN (D.REASON in (2, 3, 4, 5, 6, 7, 8, 9, 504)) Then 1 Else 0 END) as CONDITION_2
FROM A_VIEW A
LEFT OUTER JOIN B_VIEW B on B.A_GLOBALID = A.GLOBALID
LEFT OUTER JOIN C_VIEW C on C.B_GLOBALID = B.GLOBALID
LEFT OUTER JOIN D_VIEW D on
D.C_ID = C.OBJECTID or
D.A_ID = A.OBJECTID
WHERE 1 = 1 AND A.TYPE=:TYPE0
GROUP BY A.ObjectID
В нашей тестовой среде план выполнения выглядит следующим образом:
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 207K| 439M| | 5109M (4)|110:52:41 |
| 1 | HASH GROUP BY | | 207K| 439M| 4872M| 5109M (4)|110:52:41 |
| 2 | MERGE JOIN OUTER | | 2182K| 4631M| | 5108M (4)|110:52:15 |
|* 3 | HASH JOIN OUTER | | 2182K| 4604M| 449M| 290K (2)| 00:00:23 |
|* 4 | HASH JOIN OUTER | | 2182K| 424M| 113M| 244K (2)| 00:00:20 |
|* 5 | HASH JOIN OUTER | | 741K| 104M| 41M| 56975 (2)| 00:00:05 |
|* 6 | TABLE ACCESS BY INDEX ROWID BATCHED| A | 610K| 34M| | 37393 (2)| 00:00:03 |
|* 7 | INDEX RANGE SCAN | I1439TYPE | 1627K| | | 2801 (3)| 00:00:01 |
|* 8 | INDEX FAST FULL SCAN | IDX$$_973C0001 | 2785K| 236M| | 6227 (3)| 00:00:01 |
| 9 | VIEW | index$_join$_009 | 7908K| 422M| | 159K (2)| 00:00:13 |
|* 10 | HASH JOIN | | | | | | |
|* 11 | INDEX RANGE SCAN | IDX$$_973C0002 | 7908K| 422M| | 54686 (2)| 00:00:05 |
|* 12 | INDEX FAST FULL SCAN | GDB_CT2_265 | 7908K| 422M| | 67651 (2)| 00:00:06 |
| 13 | VIEW | VW_SSQ_1 | 285K| 547M| | 3965 (6)| 00:00:01 |
| 14 | SORT GROUP BY | | 285K| 2511K| 19M| 3965 (6)| 00:00:01 |
| 15 | TABLE ACCESS FULL | D | 995K| 8753K| | 2320 (3)| 00:00:01 |
| 16 | BUFFER SORT | | 1 | 13 | | 5109M (4)|110:52:41 |
| 17 | VIEW | VW_LAT_DAF4C663 | 1 | 13 | | 2340 (3)| 00:00:01 |
|* 18 | TABLE ACCESS FULL | D | 1 | 121 | | 2340 (3)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ITEM_1"(+)="OBJECTID")
4 - access("B_GLOBALID"(+)="GLOBALID")
5 - access("A_GLOBALID"(+)="GLOBALID")
6 - filter("GDB_TO_DATE"=TIMESTAMP' 9999-12-31 23:59:59.000000000')
7 - access("TYPE"=TO_NUMBER(:TYPE0))
8 - filter("GDB_TO_DATE"(+)=TIMESTAMP' 9999-12-31 23:59:59.000000000')
10 - access(ROWID=ROWID)
11 - access("GDB_TO_DATE"=TIMESTAMP' 9999-12-31 23:59:59.000000000')
12 - filter("GDB_TO_DATE"=TIMESTAMP' 9999-12-31 23:59:59.000000000')
18 - filter("D"."A_ID"="OBJECTID" OR "D"."C_ID"="OBJECTID")
И план выполнения производственной среды:
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 303K| 643M| | 14M (1)| 00:18:57 |
| 1 | HASH GROUP BY | | 303K| 643M| 9G| 14M (1)| 00:18:57 |
| 2 | MERGE JOIN OUTER | | 4509K| 9568M| | 13M (1)| 00:18:03 |
|* 3 | HASH JOIN OUTER | | 2254K| 4756M| 464M| 310K (2)| 00:00:25 |
|* 4 | HASH JOIN OUTER | | 2254K| 438M| 121M| 257K (2)| 00:00:21 |
|* 5 | HASH JOIN OUTER | | 797K| 112M| 43M| 62800 (2)| 00:00:05 |
|* 6 | TABLE ACCESS BY INDEX ROWID BATCHED | A | 643K| 36M| | 41711 (2)| 00:00:04 |
|* 7 | INDEX RANGE SCAN | I1439TYPE | 1830K| | | 3213 (3)| 00:00:01 |
|* 8 | INDEX FAST FULL SCAN | IDX$$_973C0001 | 2965K| 251M| | 6889 (3)| 00:00:01 |
| 9 | VIEW | index$_join$_009 | 8125K| 433M| | 166K (2)| 00:00:14 |
|* 10 | HASH JOIN | | | | | | |
|* 11 | INDEX RANGE SCAN | IDX$$_973C0002 | 8125K| 433M| | 56959 (1)| 00:00:05 |
|* 12 | INDEX FAST FULL SCAN | GDB_CT2_265 | 8125K| 433M| | 70534 (2)| 00:00:06 |
| 13 | VIEW | VW_SSQ_1 | 334K| 640M| | 4902 (6)| 00:00:01 |
| 14 | SORT GROUP BY | | 334K| 2939K| 23M| 4902 (6)| 00:00:01 |
| 15 | TABLE ACCESS FULL | D | 1203K| 10M| | 2926 (3)| 00:00:01 |
| 16 | BUFFER SORT | | 2 | 26 | | 14M (1)| 00:18:57 |
| 17 | VIEW | VW_LAT_DAF4C663 | 2 | 26 | | 6 (0)| 00:00:01 |
| 18 | VIEW | VW_ORE_DF336D34 | 2 | 26 | | 6 (0)| 00:00:01 |
| 19 | UNION-ALL | | | | | | |
| 20 | TABLE ACCESS BY INDEX ROWID BATCHED| D | 1 | 9 | | 3 (0)| 00:00:01 |
|* 21 | INDEX RANGE SCAN | G1126CID | 1 | | | 2 (0)| 00:00:01 |
|* 22 | TABLE ACCESS BY INDEX ROWID BATCHED| D | 1 | 15 | | 3 (0)| 00:00:01 |
|* 23 | INDEX RANGE SCAN | G1126AID | 1 | | | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("ITEM_1"(+)="OBJECTID")
4 - access("B_GLOBALID"(+)="GLOBALID")
5 - access("A_GLOBALID"(+)="GLOBALID")
6 - filter("GDB_TO_DATE"=TIMESTAMP' 9999-12-31 23:59:59.000000000')
7 - access("TYPE"=TO_NUMBER(:TYPE0))
8 - filter("GDB_TO_DATE"(+)=TIMESTAMP' 9999-12-31 23:59:59.000000000')
10 - access(ROWID=ROWID)
11 - access("GDB_TO_DATE"=TIMESTAMP' 9999-12-31 23:59:59.000000000')
12 - filter("GDB_TO_DATE"=TIMESTAMP' 9999-12-31 23:59:59.000000000')
21 - access("D"."C_ID"="OBJECTID")
22 - filter(LNNVL("D"."C_ID"="OBJECTID"))
23 - access("D"."A_ID"="OBJECTID")
Как вы можете видеть, наша производственная Oracle на самом деле понимает, что условие или условие последнего левого соединения может быть расширено, и выполняет lnnvl-фильтр для индексированного доступа в обоих условиях, который всегда должен быть НАМНОГО более эффективным, чем полный доступ к таблице с нашим объемы данных. В нашей тестовой среде Oracle, похоже, так не считает, и всегда выполняет полное сканирование таблицы для последнего соединения,
что абсолютно снижает производительность запроса, примерно в 100 раз замедляя его выполнение.
Откуда может возникнуть эта разница в поведении?
PS Я знаю, что условное левое соединение может быть переписано в два отдельных объединения для индексированного доступа при обоих условиях, но в данный момент я более склонен выяснить причину различия в поведении и можно ли это исправить в Сам Оракул.
ОБНОВЛЕНИЕ 1:
ПОКАЗАТЬ ОПТИМИЗАТОР ПАРАМЕТРОВ:
NAME TYPE VALUE
-------------------------------------------------- ----------- ----------------------------------------------------------------------------------------------------
optimizer_adaptive_plans boolean TRUE
optimizer_adaptive_reporting_only boolean FALSE
optimizer_adaptive_statistics boolean FALSE
optimizer_capture_sql_plan_baselines boolean FALSE
optimizer_dynamic_sampling integer 4
optimizer_features_enable string 12.2.0.1
optimizer_index_caching integer 0
optimizer_index_cost_adj integer 100
optimizer_inmemory_aware boolean TRUE
optimizer_mode string ALL_ROWS
optimizer_secure_view_merging boolean TRUE
optimizer_use_invisible_indexes boolean FALSE
optimizer_use_pending_statistics boolean FALSE
optimizer_use_sql_plan_baselines boolean TRUE