Базовый курс SQL. Комбинированные запросы. Соединения. UNION

Forums:

[к оглавлению]

В обычном SQL-запросе мы используем ключевое слово SELECT один раз, извлекая данные из одной или нескольких таблиц. Но возможности SQL позволяют также скомбинировать несколько таких запросов в один с помощью оператора UNION, чтобы вернуть результат в едином наборе данных. Эти запросы называют комбинированными запросами, или соединениями.

Когда используются комбинированные запросы:

  • когда нужно получить данные, структурированные одинаковым способом, но из разных таблиц
  • когда нужно сделать несколько запросов к одной и той же таблице и получить данные в едином наборе

Многократные условия WHERE

В основном получается так, что результат комбинированного запроса к одной таблице аналогичен запросу к ней с множественными условиями WHERE. Поэтому любой запрос с несколькими условиями WHERE можно также считать комбинированным запросом.

Создание соединений

Синтаксис соединений очень простой: достаточно составить все запросы SELECT, которые вам требуются и добавить между ними UNION.

Рассмотрим соединение на примере. Предположим нам необходимо получить список иностранных студентов и добавить к нему тех, кто обучается в группе '117F'. Можно составить сложный запрос с условиями WHERE, но в данном случае удобнее использовать соединение.

Для начала рассмотрим отдельно части запроса:

SELECT student_surname
FROM Students
WHERE student_country != 'РФ';

Получим:

student_surname
------------------------
Сейдинай                                          
Адамченко                                         
Легран  
SELECT student_surname
FROM Students
WHERE group_id = (SELECT group_id 
                                  FROM Groups
                                  WHERE group_name = '177F');

Получим второй список:

student_surname
------------------------                                      
Легран                                            
Егорова                                           
Грошев         

Теперь сделаем то же, но с помощью соединения UNION:

SELECT student_surname
FROM Students
WHERE student_country != 'РФ'
UNION
SELECT student_surname
FROM Students
WHERE group_id = (SELECT group_id 
                                  FROM Groups
                                  WHERE group_name = '117F');

Получим необходимый нам результат в одном наборе данных:

student_surname
------------------------
Сейдинай                                          
Легран                                            
Егорова                                           
Грошев                                            
Адамченко                                         

И для сравнения рассмотрим тот же запрос с множественным условием WHERE:

SELECT student_surname
FROM Students
WHERE student_country != 'РФ' 
    OR group_id = (SELECT group_id 
                                  FROM Groups
                                  WHERE group_name = '117F');

Запрос с помощью соединения может показаться громоздким по сравнению с множественным WHERE, но зато он наглядно демонстрирует смысл того, что необходимо получить. С точки зрения поддержки кода часто это оправданный шаг, особенно если условия более сложные.

Сколько запросов можно соединять?

В стандарте SQL нет ограничений на число запросов SELECT, соединяемых с помощью UNION. Однако всегда лучше уточнять такие нюансы в документации конкретной СУБД.

Производительность соединений

В большинстве СУБД есть встроенный оптимизатор запросов, который комбинирует все SELECT в соединении. Это означает, что разницы между несколькими условиями в одном запросе и несколькими целыми запросами нет. Но это только теоретически. На практике всегда лучше тестировать оба варианта, чтобы опытным путём убедиться в том, что выбран оптимальный метод.

Правила применения UNION

Определим некоторые правила, которые следует соблюдать, используя UNION:

  • Оператор UNION должен разделять все запросы SELECT, сколько бы их не было
  • Соединения можно использовать только для получения одних и тех же полей таблиц. Т.е. имена столбцов и применяемые итоговые функции должны быть одинаковыми во всех запросах. В некоторых СУБД значение имеет даже их порядок.
  • Типы данных запрашиваемых столбцов должны быть по крайней мере совместимы. Т.е. тип данных должен быть либо тот же, либо такой, который СУБД сможет преобразовать автоматически (например, различные форматы числа или даты)

Включить повторяющиеся строки

Рассмотрим внимательнее результаты отдельных запросов из предыдущего примера. Первый запрос возвращает 3 строки, второй - тоже 3. Но при их соединении, мы получаем 5 строк, а не 6. Так происходит потому, что UNION по умолчанию исключает из выборки одинаковые результаты (так же сработал бы и запрос с несколькими условиями WHERE). В нашем случае повторяющаяся строка - фамилия студента 'Легран'.

Если нам по каким-то причинам нужно получить все строки таблиц без исключения, применим оператор UNION ALL:

SELECT student_surname
FROM Students
WHERE student_country != 'РФ'
UNION ALL 
SELECT student_surname
FROM Students
WHERE group_id = (SELECT group_id
                  FROM Groups
                  WHERE group_name = '117F');

В результате 6 строк:

student_surname
-----------------------
Сейдинай                                          
Адамченко                                         
Легран                                            
Егорова                                           
Легран                                            
Грошев                  

UNION ALL или WHERE

В отличие от предыдущих примеров, WHERE никак не сможет заменить UNION ALL. Так что, если вы хотите получить полный список строк, вместе с дубликатами, используйте соединения.

Сортировка в соединениях

Т.к. результаты всех запросов соединения имеют одинаковую структуру, их с лёгкостью можно отсортировать. Оператор ORDER BY при этом должен быть один для всех запросов и находиться после последнего оператора SELECT.
Если вы примените сортировку несколько раз, СУБД выдаст ошибку.

Давайте дополним наше соединение:

SELECT student_surname
FROM Students
WHERE student_country != 'РФ'
UNION
SELECT student_surname
FROM Students
WHERE group_id = (SELECT group_id
                  FROM Groups
                  WHERE group_name = '117F')
ORDER BY student_surname;

Фамилии будут отсортированы по алфавиту:

student_surname
-----------------------
Адамченко                                         
Грошев                                            
Егорова                                           
Легран                                            
Сейдинай      

Несмотря на то, что фактически ORDER BY принадлежит только последнему запросу SELECT, СУБД применит его ко всей полученной выборке.

Какие ещё бывают комбинированные запросы?

В некоторых СУБД есть и другие типы комбинированных соединений. Они создаются с помощью операторов EXCEPT, или MINUS, (позволяет извлечь строки, которые есть в первой таблице, но отсутствуют во второй) и INTERSECT (позволяет извлечь только строки, имеющиеся в обоих таблицах). Но на практике для этих целей чаще используют объединения.

Соединения разных таблиц

На этом уроке мы рассмотрели соединения запросов к одной и той же таблице, но это не предел. Также UNION можно использовать для разных таблиц - необходимо только следить за совместимостью типов данных. В таких случаях, если имена столбцов различаются, для их соединения придётся использовать псевдонимы.

Итак, сегодня мы узнали, как можно упростить сложные запросы с множественными условиями WHERE. И как с помощью оператора UNION получить результаты запросов из одной или нескольких таблиц в одной выборке.

Следующая страница. Запись данных в таблицы
Оглавление.