Соединение таблиц внутри скобок. Роль и преимущества
Столкнулся с незнакомым кейсом, который можно описать простым примером:
WITH A AS
(
SELECT 1 A, 6 B FROM DUAL UNION ALL
SELECT 2 A, 5 B FROM DUAL UNION ALL
SELECT 3 A, 4 B FROM DUAL UNION ALL
SELECT 4 A, 3 B FROM DUAL UNION ALL
SELECT 5 A, null B FROM DUAL UNION ALL
SELECT 6 A, 1 B FROM DUAL
)
SELECT '||A1||', A1.*,
'||A2||', A2.*,
'||A3||', A3.*
FROM A A1
JOIN (A A2
LEFT JOIN A A3 ON A2.A=A3.B)
ON A1.A = A3.B;
Этот код полностью работает в Оракл 12c, но не работает, например, в импале (без FROM DUAL, конечно).
Суть в том, что я впервые вижу JOIN внутри скобок. Причем, эти скобки не получают своего псевдонима и соединяются с другими таблицами со ссылкой на имя одной из таблиц внутри скобок.
Пытаюсь понять, почему так сделано и какие это дает выгоды. Является ли это принуждением оптимизатора выполнить сперва JOIN таблиц А2 и А3? Какие преимущества такой JOIN может иметь перед традиционным подходом?
WITH A AS (
SELECT 1 A, 6 B FROM DUAL UNION ALL
SELECT 2 A, 5 B FROM DUAL UNION ALL
SELECT 3 A, 4 B FROM DUAL UNION ALL
SELECT 4 A, 3 B FROM DUAL UNION ALL
SELECT 5 A, null B FROM DUAL UNION ALL
SELECT 6 A, 1 B FROM DUAL
)
SELECT '||A1||', A1.*,
'||A2||', A2.*,
'||A3||', A3.*
FROM A A1
JOIN A A3 ON A1.A = A3.B
LEFT JOIN A A2 ON A2.A=A3.B;
Ответы (1 шт):
Роль скобок в соединени трёх и более таблиц та же, что и в любом составном выражении - изменить порядок выполнения. В документации (см. join_clause) об этом сказано следующее:
When you join more than two row sources, you can use parentheses to override default precedence. For example, the following syntax:
SELECT ... FROM a JOIN (b JOIN c) ...results in a join of
bandc, and then a join of that result set witha.
Порядок по умолчанию - слева на право. Причём, оптимизатор вправе изменить этот порядок исходя из условия соединения, наличия индексов и мн. др., но если в соединении есть внешнее (outer join), то порядок сохранится, так как результат будет зависеть от порядка выполнения.
Для лучшего понимания, воспроизводимый пример, где порядок выполнения соединений играет роль, он изменяется с заключением одного из соединений в скобки:
with t (id, val) as (
select rownum, column_value
from sys.odciVarchar2List ('a', 'b', 'c')
)
select a.id, c.val val
from t a
left join t b on b.id=a.id and b.val != 'c'
join t c on c.id=b.id
union all
select null, '#1 ^^^; #2 vvv' from dual
union all
select a.id, c.val val
from t a
left join (
t b join t c on c.id=b.id
) on b.id=a.id and b.val != 'c';
ID VAL
---------- ----------------
1 a
2 b
#1 ^^^; #2 vvv
1 a
2 b
3