Авторские статьи Sql. Явные операции соединения. Как готовить и с чем есть.

Discussion in 'Статьи' started by VDShark, 3 May 2008.

  1. VDShark

    VDShark Elder - Старейшина

    Joined:
    1 Feb 2007
    Messages:
    260
    Likes Received:
    158
    Reputations:
    62
    sql. Явные операции соединения. Как готовить и с чем есть.
    Содержание:​
    1. Типы соединений
      1. Перекрестное соединение
      2. Естественное соединение
      3. Соединение объединения
      4. Соединение посредством предиката
      5. Соединение посредством имен столбцов
    2. Примеры
      1. INNER JOIN
      2. LEFT JOIN
      3. RIGHT JOIN
      4. FULL JOIN
      5. UNION JOIN
    Введение
    Написать эту статью меня сподвиг следующий факт: очень многие не знают как применять JOIN'ы, а если и применяют — то в многих случаях не рационально, а то и неправильно (ведь в неумелых руках не так то и сложно завалить сервер, если таблицы достаточно большие). Хотя сама по себе тема не очень сложная.​
    В разных СУБД разный уровень поддержки этих операций. Например PostgreSQL (что и неудивительно, ведь эта СУБД — университетская разработка, и наиболее близка к стандартам ANSI SQL... даже домены держит) поддерживает естественное соединение (чего нельзя сказать о MSSQL, по крайней мере 2005... до 2008 руки еще не дошли посмотреть, но в кратком обзоре я не встретил ничего про поддержку). Поэтому отойдем от конкретных СУБД и рассмотрим то, о чем гласит стандарт =)​
    Впервые явные операции соединения появились в стандарте SQL2 (1992 год). До этого приходилось идти обходными путями, например так:
    Code:
    SELECT * FROM A, B WHERE A.col = B.col
    
    Но соединением это нельзя назвать даже с натяжкой... И присутствовали следующие недостатки:
    • Здесь идет декартово произведение (т.е. комбинации всех строк одной таблицы, со всеми строками другой таблицы) — а потом накладывается ограничение (предикат в WHERE). Т.е. нагрузка неслабая. Допустим у нас 2 таблички: в одной 1000 строк, в другой 10000 строк. В итоге будет сформировано 1000*10000 = 10000000 строк а потом отобраны нужные нам. Видно что не самый лучший подход.
    • Таким образом можно было реализовать не все типы и виды соединений, которые возможно реализовать с помощью явных операций соединения.
    Но с появлением этих операций в стандарте, ситуация улучшилось. Рассмотрим что же там такого было.
    Типы соединений
    Все типы соединений будут рассмотрены на 2-х таблицах: назовем их A и B. Весь запрос буду опускать, буду писать только то, что идет после FROM.​
    Code:
    Таблица A:
    -------------------------------------------------
    |	[I][B]a[/B][/I]	|	[I][B]b[/B][/I]	|	[I][B]c[/B][/I]	|
    -------------------------------------------------
    |	1	|	1	|	1	|
    -------------------------------------------------
    |	1	|	2	|	1	|
    -------------------------------------------------
    |	2	|	1	|	1	|
    -------------------------------------------------
    |	2	|	2	|	2	|
    -------------------------------------------------
    Таблица B:
    -------------------------------------------------
    |	[I][B]a[/B][/I]	|	[I][B]b[/B][/I]	|	[I][B]d[/B][/I]	|
    -------------------------------------------------
    |	2	|	1	|	1	|
    -------------------------------------------------
    |	2	|	2	|	1	|
    -------------------------------------------------
    |	2	|	2	|	2	|
    -------------------------------------------------
    
    1. Перекрестное соединение
    Code:
    ... A CROSS JOIN B
    По сути это то же декартово произведение... Было введено думаю только для удобства и читаемости :)

    2. Естественное соединение
    Code:
    ... A [Natural][<вид соединения>] JOIN B
    Естественное соединение означает операцию соединения по всем столбцам с совпадающими именами, находящихся в соединяемых таблицах. Т.е. на выходе будут только те строки, в которых значения всех столбцов с совпадающими именами совпадают попарно.
    Разумеется, столбцы с одинаковыми именами должны быть определены на совместимых типах данных.
    Code:
    Результат:
    -----------------------------------------------------------------
    |	[I][B]a[/B][/I]	|	[I][B]b[/B][/I]	|	[I][B]c[/B]	[/I]|	[I][B]d[/B][/I]	|
    -----------------------------------------------------------------
    |	2	|	1	|	1	|	1	|
    -----------------------------------------------------------------
    |	2	|	2	|	2	|	1	|
    -----------------------------------------------------------------
    |	2	|	2	|	2	|	2	|
    -----------------------------------------------------------------
    

    3. Соединение объединения
    Code:
    	... A UNION JOIN B
    Для читающих по диагонали или для тех кто не понял: соединение UNION и оператор UNION — это абсолютно разные понятия :)
    Пример будет ниже.​

    4. Соединение посредством предиката
    Code:
    	... A [<вид соединения>] JOIN B ON <предикат>
    Думаю все знают что предикат? :) Если нет — вот небольшое определение:
    Предикат (n-местный, или n-арный) — это функция с областью значений {0,1} (или «Истина» и «Ложь»).
    Если все равно не понятно — ну что ж...
    http://ru.wikipedia.org/wiki/Предикат
    Code:
    A JOIN B ON A.a <> B.d
    -------------------------------------------------------------------------------------------------
    |	[I][B]a[/B][/I]	|  [I][B]A.b[/B][/I]    	|  [I][B]A.c[/B][/I]	        |  [I][B]B.a[/B][/I]          |  [I][B]B.b[/B][/I]	        |	[I][B]d[/B][/I]	|
    -------------------------------------------------------------------------------------------------
    |	1	|	1	|	1	|	2	|	2	|	2	|
    -------------------------------------------------------------------------------------------------
    |	1	|	2	|	1	|	2	|	2	|	2	|
    -------------------------------------------------------------------------------------------------
    |	2	|	1	|	1	|	2	|	1	|	1	|
    -------------------------------------------------------------------------------------------------
    |	2	|	1	|	1	|	2	|	2	|	1	|
    -------------------------------------------------------------------------------------------------
    |	2	|	2	|	2	|	2	|	1	|	1	|
    -------------------------------------------------------------------------------------------------
    |	2	|	2	|	2	|	2	|	2	|	1	|
    -------------------------------------------------------------------------------------------------
    

    5. Соединение посредством имен столбцов
    Code:
    	... A[<вид соединения>] JOIN B USING (<список столбцов>)
    Где в списке столбцов указываются имена столбцов, по которым ведется соединение, через запятую :)

    Выше во многих типах соединения использовался <вид соединения>... Что ж это за зверь такой? А это на самом деле вот что:
    Code:
    <вид соединения> ::= INNER |{{LEFT|RIGHT|FULL}[OUTER]}
    Ключевое слово OUTER (т.е. внешнее) не является обязательным и опять таки сделано для удобства.
    Это соединение позволяет ограничить естественное соединение только теми столбцами с одинаковыми именами, которые нам необходимы.​

    Примеры
    Примеры будем рассматривать на двух следующих таблицах:
    Code:
    Таблица A:
    -----------------------------------------
    |	 [I][B]id[/B][/I]		|	 [I][B]nick[/B][/I]	|
    -----------------------------------------
    |	  1		|  VDShark	|
    -----------------------------------------
    |	  2		|  Helios	|
    -----------------------------------------
    |	  3		|  ArdeOS	|
    -----------------------------------------
    
    Таблица B:
    ---------------------------------
    |	 [I][B]nick[/B][/I]	|  [I][B]contact[/B][/I]	|
    ---------------------------------
    | VDShark	|  	55555	|
    ---------------------------------
    | Helios	|  	77777	|
    ---------------------------------
    | Helios	|  	88888	|
    ---------------------------------
    | Mobile	|  	44444	|
    ---------------------------------
    
    Это у нас допустим база людей, состоящая из 2-х таблиц: таблица с никами и таблица с контактами.
    Судя по таблице ArdeOS скрывается, а Helios мажор — у него несколько контактов :D

    1. A INNER JOIN B
    Code:
    ---------------------------------------------------------
    |	 [I][B]id[/B][/I]		|	[I][B]nick[/B][/I]	|  [I][B]contact[/B][/I]	|
    ---------------------------------------------------------
    |	  1		|  VDShark	|	55555	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	77777	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	88888	|
    ---------------------------------------------------------
    

    2. A LEFT JOIN B
    Левое соединение включает в себя все строки таблицы A (таблицы, записанной слева от JOIN) + совпадающие значения из таблицы B. Для строк из таблицы A, которым не нашлось совпадения в таблице B в столбцах из таблицы B помещаются NULL-значения
    Code:
    ---------------------------------------------------------
    |	 [I][B]id[/B][/I]		|	[I][B]nick[/B][/I]	|  [I][B]contact[/B][/I]	|
    ---------------------------------------------------------
    |	  1		|  VDShark	|	55555	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	77777	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	88888	|
    ---------------------------------------------------------
    |	  3		|  ArdeOS	|	NULL	|
    ---------------------------------------------------------
    

    3. A RIGHT JOIN B
    Правое соединение обратно левому, т.е. все столбцы из таблицы B (справа от JOIN) и те строки из таблицы A, для которых совпадают значения в одноименных столбцах.
    Code:
    ---------------------------------------------------------
    |	 [I][B]id[/B][/I]		|	[I][B]nick[/B][/I]	|  [I][B]contact[/B][/I]	|
    ---------------------------------------------------------
    |	  1		|  VDShark	|	55555	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	77777	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	88888	|
    ---------------------------------------------------------
    |   NULL	        |  Mobile	|	44444	|
    ---------------------------------------------------------
    

    4. A FULL JOIN B
    Это объединение левого и правого соединения. Присутствуют все строки из обеих таблиц. Если строки совпадают по столбцам — то они заполнены, если же нет — то в отсутствующие заносятся NULL-значения.
    Code:
    ---------------------------------------------------------
    |	 [I][B]id[/B][/I]		|	[I][B]nick[/B][/I]	|  [I][B]contact[/B][/I]	|
    ---------------------------------------------------------
    |	  1		|  VDShark	|	55555	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	77777	|
    ---------------------------------------------------------
    |	  2		|  Helios	|	88888	|
    ---------------------------------------------------------
    |	  3		|  ArdeOS	|	NULL	|
    ---------------------------------------------------------
    |   NULL	        |  Mobile	|	44444	|
    ---------------------------------------------------------
    

    5. A UNION JOIN B
    Это соединение является обратным по отношению к INNER, т.е. включает только те строки, для которых не найдено совпадений.
    Code:
    ---------------------------------------------------------
    |	 [I][B]id[/B][/I]		|	[I][B]nick[/B][/I]	|  [I][B]contact[/B][/I]	|
    ---------------------------------------------------------
    |	  3		|  ArdeOS	|	NULL	|
    ---------------------------------------------------------
    |   NULL	        |  Mobile	|	44444	|
    ---------------------------------------------------------
    

    Вывод.
    Из вышесказанного видно: оказывается не так то оно и сложно. Главное иметь хоть немного усидчивости что бы разобраться.​
     
    11 people like this.
  2. .Slip

    .Slip Elder - Старейшина

    Joined:
    16 Jan 2006
    Messages:
    1,571
    Likes Received:
    977
    Reputations:
    783
    Не знаю насколько эта статья полезней манулов в книгах/на сайтах, не знаю чем она поможет здешней аудитории которая только подставляет кавычки. Но оформлена хорошо.