> For the complete documentation index, see [llms.txt](https://evgenii-afanasev.gitbook.io/ruby-course/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://evgenii-afanasev.gitbook.io/ruby-course/12_database/4_subquery_join_union.md).

# Работа с несколькими таблицами с помощью подзапросов, JOIN, UNION

Как это часто бывает, нам в работе с данными необходимо взаимодействовать с несколькими таблицами. В этом уроке мы подробно обсудим основные способы объединения данных из нескольких таблиц, каждый из которых решает своб проблему.

## Подзапросы

Подзапрос это возможность использовать запрос внутри другого запроса для использования его резульата как значение.

Например, у нас есть идентификатор товара равный 100500, номер телефона клиента равный 798755551122 и мы хотим создать заказ для клиента с данным номером телефона.

Для этого сначала нам необходимо понять как получить идентификатор клиента по номеру телефона:

```sql
SELECT id FROM customers WHERE phone = "798755551122"
```

После этого мы можем использовать этот запрос внутри запроса на добавление:

```sql
INSERT INTO orders(item_id, customer_id, date_added)
VALUES (100500, (SELECT id FROM customers WHERE phone = "798755551122"), CURRENT_TIMESTAMP) -- CURRENT_TIMESTAMP возвращает текущее время по GMT
```

Также мы можем работать с результатом в виде списка, например, чтобы использовать его в качестве аргумента `WHERE x IN ...`.

Например я хочу посмотреть список заказов покупателей, чье имя John.

Сначала напишем запрос для получения идентификаторов покупателей с именем John:

```sql
SELECT FROM customers WHERE first_name = "John"
```

Теперь мы можем использовать этот запрос внутри запроса на получение заказов:

```sql
SELECT * FROM orders WHERE customer_id in (SELECT FROM customers WHERE first_name = "John")
```

## JOIN

### INNER JOIN

оператор JOIN используется для объеденения таблиц по признаку. Объединяя таблиц друг с другом мы получаем возможность работать с одной и другой таблицей внутри одного запроса. Синтакси такого запроса выглядит следующищм образом:

```sql
SELECT a.*, b.название_колонки FROM таблица_а as a
JOIN таблица_б as б ON a.название_колонки = б.название_колонки -- JOIN по умолчанию является INNER JOIN
```

Запрос выше имеет название `INNER JOIN` и его задачи найти все данные из таблицы А, найти все данные из таблицы Б и найти их пересечение по условию в блоке `ON ...`. При этом, если у какой-нибудь строки из таблицы А нет пересечения с таблицей Б, то это значение не будет учитываться.

### LEFT/RIGHT JOIN

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

Для этого нам на помощью приходит оператор `LEFT JOIN`.

```sql
SELECT a.*, b.название_колонки FROM таблица_а as a
LEFT JOIN таблица_б as б ON a.название_колонки = б.название_колонки
```

Теперь если данные в таблице А не найдены, то значением `b.название_колонки` будет NULL.

RIGHT JOIN делает тоже самое, что и LEFT JOHN, только пропуск связи допускается для присоединяемой таблицы.

```sql
SELECT a.*, b.название_колонки FROM таблица_а as a
RIGHT JOIN таблица_б as б ON a.название_колонки = б.название_колонки
```

Если в таблице Б у колонки `б.название_колонки` есть значение, которого нет в таблице А в `a.название_колонки`, то в качестве `a.название_колонки` мы увидим NULL.

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

```sql
SELECT a.*, b.название_колонки FROM таблица_а as a
JOIN таблица_б as б ON a.название_колонки = б.название_колонки
JOIN таблица_с as с ON б.другая_колонка = с.название_колонки
```

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

Вернемся к прмеру с получением заказов пользователей с именем John. Мы можем записать этот запрос через оператор `JOIN`.

```sql
SELECT o.*, c.first_name, c.last_name FROM orders as o -- o.* это выборка всех колонок из таблицы имеющей алиас o
JOIN customers as c ON o.customer_id = c.id 
WHERE c.first_name = 'John' 
```

На данной картинке вы можете подробнее ознакомиться как можно объединять данные друг с другом используя различные виды JOIN и условия

![joins](https://2943556334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FICTxgdiQZ3xQY3MYtkcv%2Fuploads%2Fgit-blob-e2c59e7259e32716d04664058387b43c9c9c715a%2Fjoins.jpg?alt=media)

## UNION

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

Синтаксис:

```sql
SELECT первая_колонка, воторая колонка FROM таблица_а
UNION SELECT первая_колонка, воторая колонка FROM таблица_б
```

Если колонки имеют разные нзавания, то зпрос завершится ошибкой. В этом случае стоит вручную задать название. Помимо этого, ошибкой завершится запрос, если количество колонок у таблиц отличается. В этом случае нужно добавить NULL как значение колонок `SELECT NULL as название_колонки FROM таблица_а`.

Дополнительный материал:

* [Подзапросы](https://metanit.com/sql/sqlite/4.7.php)
* [JOINы](https://metanit.com/sql/sqlite/5.2.php)
* [UNION](https://metanit.com/sql/sqlite/5.4.php)
