Как работает SQL join? - PullRequest
       7

Как работает SQL join?

2 голосов
/ 13 января 2009

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

For example

(A)

Select * 
FROM TABLE1
FULL JOIN TABLE2 ON TABLE1.ID = TABLE2.ID
FULL JOIN TABLE3 ON TABLE1.ID = TABLE3.ID

And

(B)

Select * 
FROM TABLE1
FULL JOIN TABLE2 ON TABLE1.ID = TABLE2.ID
FULL JOIN TABLE3 ON TABLE2.ID = TABLE3.ID

Редактировать: я говорю о оракуле здесь. Рассмотрим некоторые записи, представленные в таблице 2 и таблице 3, но не в таблице 1, запрос A даст две строки для этой записи, а B даст только одну строку.

Ответы [ 5 ]

9 голосов
/ 13 января 2009

Оптимизатор вашей СУБД определит, как лучше всего выполнить запрос. Обычно это делается путем «оптимизации на основе затрат», когда рассматривается несколько различных планов запросов и выбирается наиболее эффективный. Если ваши два запроса логически идентичны, наиболее вероятно, что оптимизатор в конечном итоге будет использовать один и тот же план запросов, независимо от того, как вы его напишите. Фактически, в наши дни это был бы плохой оптимизатор, который создавал бы разные планы запросов, основанные на таких незначительных различиях в SQL.

Однако полные внешние объединения - это другое дело (по крайней мере, в Oracle), поскольку способ соединения столбцов влияет на результат. то есть 2 запроса не взаимозаменяемы.

Вы можете использовать AUTOTRACE в SQL Plus, чтобы увидеть разные планы:

SQL> select *
  2  from t1
  3  full join t2 on t2.id = t1.id
  4  full join t3 on t3.id = t2.id;

        ID         ID         ID
---------- ---------- ----------
                    1          1

1 row selected.


Execution Plan
----------------------------------------------------------

---------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     3 |   117 |    29  (11)|
|   1 |  VIEW                   |      |     3 |   117 |    29  (11)|
|   2 |   UNION-ALL             |      |       |       |            |
|*  3 |    HASH JOIN OUTER      |      |     2 |   142 |    15  (14)|
|   4 |     VIEW                |      |     2 |    90 |    11  (10)|
|   5 |      UNION-ALL          |      |       |       |            |
|*  6 |       HASH JOIN OUTER   |      |     1 |    91 |     6  (17)|
|   7 |        TABLE ACCESS FULL| T1   |     1 |    52 |     2   (0)|
|   8 |        TABLE ACCESS FULL| T2   |     1 |    39 |     3   (0)|
|*  9 |       HASH JOIN ANTI    |      |     1 |    26 |     6  (17)|
|  10 |        TABLE ACCESS FULL| T2   |     1 |    13 |     3   (0)|
|  11 |        TABLE ACCESS FULL| T1   |     1 |    13 |     2   (0)|
|  12 |     TABLE ACCESS FULL   | T3   |     1 |    26 |     3   (0)|
|* 13 |    HASH JOIN ANTI       |      |     1 |    26 |    15  (14)|
|  14 |     TABLE ACCESS FULL   | T3   |     1 |    13 |     3   (0)|
|  15 |     VIEW                |      |     2 |    26 |    11  (10)|
|  16 |      UNION-ALL          |      |       |       |            |
|* 17 |       HASH JOIN OUTER   |      |     1 |    39 |     6  (17)|
|  18 |        TABLE ACCESS FULL| T1   |     1 |    26 |     2   (0)|
|  19 |        TABLE ACCESS FULL| T2   |     1 |    13 |     3   (0)|
|* 20 |       HASH JOIN ANTI    |      |     1 |    26 |     6  (17)|
|  21 |        TABLE ACCESS FULL| T2   |     1 |    13 |     3   (0)|
|  22 |        TABLE ACCESS FULL| T1   |     1 |    13 |     2   (0)|
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("T3"."ID"(+)="T2"."ID")
   6 - access("T2"."ID"(+)="T1"."ID")
   9 - access("T2"."ID"="T1"."ID")
  13 - access("T3"."ID"="T2"."ID")
  17 - access("T2"."ID"(+)="T1"."ID")
  20 - access("T2"."ID"="T1"."ID")

SQL> select *
  2  from t1
  3  full join t2 on t2.id = t1.id
  4  full join t3 on t3.id = t1.id;

        ID         ID         ID
---------- ---------- ----------
                    1
                               1

2 rows selected.


Execution Plan
----------------------------------------------------------

---------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     3 |   117 |    29  (11)|
|   1 |  VIEW                   |      |     3 |   117 |    29  (11)|
|   2 |   UNION-ALL             |      |       |       |            |
|*  3 |    HASH JOIN OUTER      |      |     2 |   142 |    15  (14)|
|   4 |     VIEW                |      |     2 |    90 |    11  (10)|
|   5 |      UNION-ALL          |      |       |       |            |
|*  6 |       HASH JOIN OUTER   |      |     1 |    91 |     6  (17)|
|   7 |        TABLE ACCESS FULL| T1   |     1 |    52 |     2   (0)|
|   8 |        TABLE ACCESS FULL| T2   |     1 |    39 |     3   (0)|
|*  9 |       HASH JOIN ANTI    |      |     1 |    26 |     6  (17)|
|  10 |        TABLE ACCESS FULL| T2   |     1 |    13 |     3   (0)|
|  11 |        TABLE ACCESS FULL| T1   |     1 |    13 |     2   (0)|
|  12 |     TABLE ACCESS FULL   | T3   |     1 |    26 |     3   (0)|
|* 13 |    HASH JOIN ANTI       |      |     1 |    26 |    15  (14)|
|  14 |     TABLE ACCESS FULL   | T3   |     1 |    13 |     3   (0)|
|  15 |     VIEW                |      |     2 |    26 |    11  (10)|
|  16 |      UNION-ALL          |      |       |       |            |
|* 17 |       HASH JOIN OUTER   |      |     1 |    39 |     6  (17)|
|  18 |        TABLE ACCESS FULL| T1   |     1 |    26 |     2   (0)|
|  19 |        TABLE ACCESS FULL| T2   |     1 |    13 |     3   (0)|
|* 20 |       HASH JOIN ANTI    |      |     1 |    26 |     6  (17)|
|  21 |        TABLE ACCESS FULL| T2   |     1 |    13 |     3   (0)|
|  22 |        TABLE ACCESS FULL| T1   |     1 |    13 |     2   (0)|
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("T3"."ID"(+)="T1"."ID")
   6 - access("T2"."ID"(+)="T1"."ID")
   9 - access("T2"."ID"="T1"."ID")
  13 - access("T3"."ID"="T1"."ID")
  17 - access("T2"."ID"(+)="T1"."ID")
  20 - access("T2"."ID"="T1"."ID")

Фактически планы запросов идентичны, за исключением информации о предикате

5 голосов
/ 13 января 2009

Вы заявили о своей заинтересованности в «внутренностях», а затем задали пример, иллюстрирующий «семантику». Я отвечаю на семантику.

Рассмотрим эти таблицы.

Table1 : 1, 4, 6
Table2 : 2, 4, 5
Table3 : 3, 5, 6

В обоих примерах сначала выполняется одно и то же соединение, поэтому я выполню это здесь.

FirstResult = T1 FULL JOIN T2 : (T1, T2)
(1, null)
(4, 4)
(6, null)
(null, 2)
(null, 5)

Пример (А)

FirstResult FULL JOIN T3 ON FirstItem : (T1, T2, T3)

(1, null, null)
(4, 4, null)
(6, null, 6)   <----
(null, 2, null)
(null, 5, null)   <----
(null, null, 3)

Пример (B)

FirstResult FULL JOIN T3 ON SecondItem : (T1, T2, T3)
(1, null, null)
(4, 4, null)
(6, null, null)   <----
(null, 2, null)
(null, 5, 5)   <----
(null, null, 3)

Здесь показано, как логически получить результаты из объединений.

Для "внутренних объектов" есть нечто, называемое оптимизатором запросов, которое будет давать те же результаты, но оно сделает выбор реализации для быстрого выполнения вычислений / ввода-вывода. Эти выборы включают в себя:

  • к каким таблицам обращаться в первую очередь
  • просмотр таблицы с использованием индекса или сканирования таблицы
  • который объединяет используемый тип реализации (вложенный цикл, слияние, хеш).

Также обратите внимание: из-за того, что оптимизатор делает эти выборы и изменяет их в зависимости от того, что он считает оптимальным, порядок результатов может измениться. Порядок результатов по умолчанию всегда "самый простой". Если вы не хотите использовать порядок по умолчанию, вам нужно указать порядок в запросе.

Чтобы точно узнать, что оптимизатор будет делать с запросом (в тот момент, когда он может изменить свое решение), вам нужно просмотреть план выполнения.

4 голосов
/ 13 января 2009
0 голосов
/ 15 июля 2011

Для понимания левого / правого соединения, как они работают Проверьте это

0 голосов
/ 14 января 2009

С запросом A то, что вы получаете, включает в себя записи в таблице 1 с соответствующей записью в таблице 3 без соответствующих записей в таблице 3 (нули для столбцов t2)

С запросом B вы не получите эти записи, потому что вы переходите только от table3 до table2. Если у вас нет соответствующей записи в table2, table2.id будет нулевым и никогда не будет соответствовать table3.id

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...