Соединение таблиц внутри скобок. Роль и преимущества

Столкнулся с незнакомым кейсом, который можно описать простым примером:

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 шт):

Автор решения: 0xdb

Роль скобок в соединени трёх и более таблиц та же, что и в любом составном выражении - изменить порядок выполнения. В документации (см. 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 b and c, and then a join of that result set with a.

Порядок по умолчанию - слева на право. Причём, оптимизатор вправе изменить этот порядок исходя из условия соединения, наличия индексов и мн. др., но если в соединении есть внешнее (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                 
→ Ссылка