Использование одного и того же псевдонима таблицы в запросе дважды

Мой коллега, новичок в синтаксисе присоединения ANSI, недавно написал такой запрос:

SELECT count(*)
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t3 ON
             (t3.col_c = t1.col_c);

Обратите внимание, что table3 объединяется с table1 и table2 в разных столбцах, но два предложения JOIN используют один и тот же псевдоним таблицы для table3.

Запрос выполняется, но я не уверен в его правильности. Это правильный способ написания этого запроса?

Я думал, что соединение должно быть так:

SELECT count(*)
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b AND
             t3.col_c = t1.col_c);

Являются ли две версии функционально идентичными? У меня действительно недостаточно данных в нашей базе данных, чтобы быть уверенным.

Благодарю.

2 ответа

Решение

Первый запрос - это объединение 4 таблиц, второй - объединение 3 таблиц. Поэтому я не ожидаю, что оба запроса вернут одинаковое количество строк.

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t3 ON
             (t3.col_c = t1.col_c);

Псевдоним t3 используется только в предложении ON. Псевдоним t3 ссылается на таблицу перед ключевым словом ON. Я выяснил это, экспериментируя. Таким образом, предыдущий запрос эквивалентен

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t4 ON
             (t4.col_c = t1.col_c);

и это может быть преобразовано в традиционное соединение

SELECT *
  FROM table1 t1,
       table2 t2,
       table3 t3,
       table3 t4
where (t1.col_a = t2.col_a)
    and  (t2.col_b = t3.col_b)
    and (t4.col_c = t1.col_c);

Второй запрос

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b AND
             t3.col_c = t1.col_c);

Это также может превратиться в традиционное соединение

SELECT *
  FROM table1 t1,
    table2 t2,
    table3 t3
where (t1.col_a = t2.col_a)
    and (t2.col_b = t3.col_b)
    AND (t3.col_c = t1.col_c);

Эти запросы кажутся разными. Для доказательства их различия мы используем следующий пример:

create table table1(
    col_a number,
    col_c number
);

create table table2(
    col_a number,
    col_b number
);

create table table3(
    col_b number,
    col_c number
);

insert into table1(col_a, col_c) values(1,3);
insert into table1(col_a, col_c) values(4,3);
insert into table2(col_a, col_b) values(1,2);
insert into table2(col_a, col_b) values(4,2);
insert into table3(col_b, col_c) values(2,3);
insert into table3(col_b, col_c) values(2,5);
insert into table3(col_b, col_c) values(7,9);

commit;

Мы получаем следующий вывод

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t3 ON
             (t3.col_c = t1.col_c)


| COL_A | COL_C | COL_A | COL_B | COL_B | COL_C | COL_B | COL_C |
|-------|-------|-------|-------|-------|-------|-------|-------|
|     1 |     3 |     1 |     2 |     2 |     3 |     2 |     3 |
|     4 |     3 |     4 |     2 |     2 |     3 |     2 |     3 |
|     1 |     3 |     1 |     2 |     2 |     5 |     2 |     3 |
|     4 |     3 |     4 |     2 |     2 |     5 |     2 |     3 |




SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b AND
             t3.col_c = t1.col_c)

| COL_A | COL_C | COL_A | COL_B | COL_B | COL_C |
|-------|-------|-------|-------|-------|-------|
|     4 |     3 |     4 |     2 |     2 |     3 |
|     1 |     3 |     1 |     2 |     2 |     3 |

Количество извлеченных строк отличается и поэтому count(*) это отличается.

Использование псевдонимов было удивительным. по крайней мере для меня.

Следующий запрос работает, потому что t1 в where_clause Рекомендации table2,

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0;

Следующий запрос работает, потому что t1 в where_clause Рекомендации table1,

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_c<0;

Следующий запрос вызывает ошибку, потому что оба table1 а также table2 содержать столбец col_a,

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_a<0;

Выдается ошибка

ORA-00918: column ambiguously defined

Следующий запрос работает, псевдоним t1 относится к двум разным таблицам в одном where_clause,

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0 and t1.col_c<0;

Эти и другие примеры можно найти здесь: http://sqlfiddle.com/

Самый маленький встречный пример

Наименьший встречный пример

table1
col_a  col_c
    1      2

table2
col_a  col_b
    1      3

table3
col_b  col_c
    3      5
    6      2    

Здесь второй запрос имеет пустой набор результатов, а первый запрос возвращает одну строку. Можно показать, что count(*) второго запроса никогда не превышает count(*) первого запроса.

Более подробное объяснение

Такое поведение станет более понятным, если мы детально проанализируем следующее утверждение.

SELECT t.col_b, t.col_c
  FROM table1 t
       JOIN table2 t ON
            (t.col_b = t.col_c) ;

Ниже приведен сокращенный синтаксис этого запроса в форме Бэкуса-Наура, полученный из описаний синтаксиса в справочнике по языку SQL Oracle 12.2. Обратите внимание, что под каждой синтаксической диаграммой есть ссылка на форму Бэкуса-Наура этой диаграммы, например, Описание иллюстрации select.eps. "Сокращенный" означает, что я упустил все возможности, которые не использовались, например,g. select определяется как

select::=subquery [ for_update_clause ] ;

Наш запрос не использует необязательный for_update_clause поэтому я уменьшил правило до

select::=subquery

Единственное исключение является необязательным where-clause, Я не удалил его, чтобы использовать эти сокращенные правила для анализа вышеуказанного запроса, даже если мы добавим where_clause,

Эти сокращенные правила будут определять только подмножество всех возможных операторов выбора.

select::=subquery 
subquery::=query_block
query_block::=SELECT select_list FROM join_clause [ where_clause ]
join_clause::=table_reference  inner_cross_join_clause ...  
table_reference::=query_table_expression  t_alias query_table_expression::=table
inner_cross_join_clause::=JOIN table_reference ON condition

Таким образом, наше утверждение является query_block и join_clause имеет тип

table_reference inner_cross_join_clause

где table_reference является table1 t а также inner_cross_join_clause является JOIN table2 t ON (t.col_b = t.col_c), Многоточие ... означает, что могут быть дополнительные inner_cross_join_clauses, но нам это здесь не нужно.

в inner_cross_join_clause псевдоним t относится к table2, Только если эти ссылки не могут быть удовлетворены, псевдоним следует искать во внешней области видимости. Таким образом, все следующие выражения в условии ON являются действительными:

t.col_b = t.col_c

Вот t.col_b является table2.col_b так как t относится к псевдониму его inner_cross_join_clause, t.col_c является table1.col_c, t из inner_cross_join_clause (Ссылаясь на table2) не имеет столбца col_c поэтому будет произведен поиск внешней области и найден соответствующий псевдоним.

Если у нас есть пункт

t.col_a = t.col_a

псевдоним можно найти как псевдоним, определенный в inner_cross_join_clause к которому это ON- condition принадлежит так t будет решен в table2,

если список выбора состоит из

t.col_c, t.col_b, t.col_a

вместо * тогда join_clause будет искать псевдоним и t.col_c будет решен в table1.col_c (table2 не содержит столбца col_c), t.col_b будет решен в table2.col_b (table1 не содержит col_b) но t.col_a поднимет ошибку

ORA-00918: column ambiguously defined

потому что для select_list ни одно из определений aias не имеет предшествующего значения перед другим. Если наш запрос также имеет where_clause тогда псевдонимы разрешаются так же, как если бы они использовались в select_list,

Чем больше данных, тем больше результатов. Ваш запрос коллеги такой же, как этот.

select * from table3 where t3.col_b = 'XX'
union
select * from table3 where t3.col_c = 'YY'

или же

select * from table3 where t3.col_b = 'XX' or t3.col_c = 'YY'

пока ваш запрос такой.

select * from table3 where t3.col_b ='XX' and t3.col_c='YY'

Первый - это данные, где (xx или yy), а второй - данные, где (xx и yy)

Другие вопросы по тегам