Я столкнулся с интересным поведением ПАРАЛЛЕЛЬНОЙ подсказки, которую я пока не могу объяснить.
Существует иерархическая структура сущностей с некоторыми дополнительными параметрами, например - тип. Идея запроса: выбрать все объекты, которые не имеют прямых и косвенных потомков определенного типа.
Образец данных:
create table parallel_hierarchy_test (
"ID" NUMBER(20,0),
"PARENT_ID" NUMBER(20,0),
"TYPE" NUMBER(20,0)
);
insert into parallel_hierarchy_test
select 0, null, 2 from dual
union all
select 1, 0, 2 from dual
union all
select 2, 0, 2 from dual
union all
select 3, 0, 2 from dual
union all
select 4, 1, 2 from dual
union all
select 5, 1, 2 from dual
union all
select 6, 4, 2 from dual
union all
select 7, 6, 1 from dual
union all
select 8, 2, 1 from dual
union all
select 9, 8, 1 from dual;
/* (id, type):
(0, 2)
--(1, 2)
----(4, 2)
------(6, 2)
--------(7, 1)
----(5, 2)
--(2, 2)
----(8, 1)
------(9, 1)
--(3, 2)
*/
и не самый лучший запрос, который я видел:
SELECT /*+ PARALLEL(2) */
t3.id
FROM parallel_hierarchy_test t3
WHERE
t3.id NOT IN (
SELECT t2.id
FROM parallel_hierarchy_test t2
START WITH t2.id IN (
SELECT
t1_2.id
FROM
(SELECT t1.id, t1.type
FROM parallel_hierarchy_test t1
START WITH t1.id = 0
CONNECT BY PRIOR t1.id = t1.parent_id
) t1_2
WHERE t1_2.type = 1)
CONNECT BY PRIOR t2.parent_id = t2.id
);
Этот скрипт, но без параллельной подсказки, возвращает ожидаемые идентификаторы: 3, 5.
Но параллельно он возвращает все идентификаторы: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
План выполнения с параллелью здесь:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:02.01 | 9 | | | |
| 1 | PX COORDINATOR | | 1 | | 10 |00:00:02.01 | 9 | | | |
| 2 | PX SEND QC (RANDOM) | :TQ40001 | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 3 | HASH JOIN ANTI NA | | 0 | 10 | 0 |00:00:00.01 | 0 | 2616K| 2616K| 1491K (0)|
| 4 | PX BLOCK ITERATOR | | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 5 | TABLE ACCESS FULL | PARALLEL_HIERARCHY_TEST | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 6 | BUFFER SORT | | 0 | | 0 |00:00:00.01 | 0 | 2048 | 2048 | |
| 7 | PX RECEIVE | | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 8 | PX SEND BROADCAST | :TQ40000 | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 9 | VIEW | VW_NSO_1 | 1 | 10 | 0 |00:00:00.01 | 6 | | | |
|* 10 | CONNECT BY WITH FILTERING (UNIQUE) | | 1 | | 0 |00:00:00.01 | 6 | 1024 | 1024 | |
| 11 | TABLE ACCESS BY INDEX ROWID | PARALLEL_HIERARCHY_TEST | 1 | | 0 |00:00:00.01 | 6 | | | |
| 12 | PX COORDINATOR | | 1 | | 1 |00:00:00.01 | 6 | | | |
| 13 | PX SEND QC (RANDOM) | :TQ30002 | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 14 | HASH JOIN SEMI BUFFERED | | 0 | 10 | 0 |00:00:00.01 | 0 | 2297K| 2297K| 1388K (0)|
| 15 | BUFFER SORT | | 0 | | 0 |00:00:00.01 | 0 | 4096 | 4096 | |
| 16 | PX RECEIVE | | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 17 | PX SEND HASH | :TQ30000 | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 18 | VIEW | | 1 | 10 | 3 |00:00:00.01 | 3 | | | |
|* 19 | CONNECT BY NO FILTERING WITH START-WITH| | 1 | | 10 |00:00:00.01 | 3 | 2048 | 2048 | 2048 (0)|
| 20 | PX COORDINATOR | | 1 | | 10 |00:00:00.01 | 3 | | | |
| 21 | PX SEND QC (RANDOM) | :TQ20000 | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 22 | PX BLOCK ITERATOR | | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 23 | TABLE ACCESS FULL | PARALLEL_HIERARCHY_TEST | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 24 | PX RECEIVE | | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 25 | PX SEND HASH | :TQ30001 | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
| 26 | PX BLOCK ITERATOR | | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 27 | TABLE ACCESS FULL | PARALLEL_HIERARCHY_TEST | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
|* 28 | HASH JOIN | | 0 | | 0 |00:00:00.01 | 0 | 1160K| 1160K| |
| 29 | CONNECT BY PUMP | | 0 | | 0 |00:00:00.01 | 0 | | | |
| 30 | TABLE ACCESS FULL | PARALLEL_HIERARCHY_TEST | 0 | 10 | 0 |00:00:00.01 | 0 | | | |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T3"."ID"="ID")
5 - access(:Z>=:Z AND :Z<=:Z)
10 - access("T2"."ID"=PRIOR NULL)
14 - access("T2"."ID"="T1_2"."ID")
18 - filter("T1_2"."TYPE"=1)
19 - access("T1"."PARENT_ID"=PRIOR NULL)
filter("T1"."ID"=0)
23 - access(:Z>=:Z AND :Z<=:Z)
27 - access(:Z>=:Z AND :Z<=:Z)
28 - access("T2"."ID"=PRIOR NULL)
Конечно, есть способы выбора по другому запросу, но я хочу понять, почему вышеуказанный запрос возвращает неожиданный результат с подсказкой PARALLEL.