JavaRush/Курсы/Модуль 4. Работа с БД/Выборка данных из нескольких таблиц

Выборка данных из нескольких таблиц

Открыта

Зависимая таблица

Теперь немного усложним наши запросы. Добавим в нашу базу данных новую таблицу task с задачами для наших сотрудников. И посмотрим, какие в ней есть записи:

SELECT * FROM task

Результат такого запроса:

id emploee_id name deadline
1 1 Исправить багу на фронтенде 2022-06-01
2 2 Исправить багу на бэкенде 2022-06-15
3 5 Купить кофе 2022-07-01
4 5 Купить кофе 2022-08-01
5 5 Купит кофе 2022-09-01
6 (NULL) Убрать офис (NULL)
7 4 Наслаждаться жизнью (NULL)
8 6 Наслаждаться жизнью (NULL)

В этой таблице есть всего 4 колонки:

  • id — уникальный номер задания (и строки в таблице);
  • employee_id — ID сотрудника из таблицы employee, на которого назначена задача;
  • name — название и описание задачи;
  • deadline — время до которого нужно выполнить задачу.

Обрати внимание на несколько нюансов. Задача N6 не имеет employee_id, у нас нет уборщицы. Задача есть, а исполнителя нет. Такое бывает.

Также у задач 6-9 нет установленного дедлайна. Такое бывает, когда задача должна делаться регулярно и беспрерывно. Например, офис нужно убирать каждый день, ну и наслаждаться жизнью тоже нужно каждый день :)

Если в одной таблице используются ID из другой таблицы, то такая таблица называется зависимой.

Запрос к нескольким таблицам

Вот мы видим в таблице task, что есть два задания “Наслаждается жизнью”. Как нам узнать, кто именно эти счастливчики?

Для этого в SQL можно выполнить запрос сразу к двум таблицам. Вообще в SQL можно выполнять запрос к любому количеству таблиц одновременно. Общий формат такого запроса:

SELECT колонки  FROM таблица1, таблица2, таблицаN

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

То есть если в первой таблице у вас 5 строк, а во второй 10, то всего у вас будет 50 строк. На языке Java этот запрос выглядел бы примерно так:

for (String row1 : table1)
{
	for (String row2 : table2)
   {
  	System.out.println(row1 + row2);
   }
}

Давай напишем наш запрос сразу к двум таблицам и посмотрим, что получится:

SELECT * FROM employee, task

И результат такого запроса:

id name occupation salary age id emploee_id name deadline
1 Иванов Иван Программист 100000 25 1 1 Исправить багу на фронтенде 2022-06-01
2 Петров Петр Программист 80000 23 1 1 Исправить багу на фронтенде 2022-06-01
3 Иванов Сергей Тестировщик 40000 30 1 1 Исправить багу на фронтенде 2022-06-01
4 Рабинович Мойша Директор 200000 35 1 1 Исправить багу на фронтенде 2022-06-01
5 Кириенко Анастасия Офис-менеджер 40000 25 1 1 Исправить багу на фронтенде 2022-06-01
6 Васька кот 1000 3 1 1 Исправить багу на фронтенде 2022-06-01
1 Иванов Иван Программист 100000 25 2 2 Исправить багу на бэкенде 2022-06-15
2 Петров Петр Программист 80000 23 2 2 Исправить багу на бэкенде 2022-06-15
3 Иванов Сергей Тестировщик 40000 30 2 2 Исправить багу на бэкенде 2022-06-15
4 Рабинович Мойша Директор 200000 35 2 2 Исправить багу на бэкенде 2022-06-15
5 Кириенко Анастасия Офис-менеджер 40000 25 2 2 Исправить багу на бэкенде 2022-06-15

Всего строк результата у нас 48, но тут я привел всего 11. Иначе просто места не хватит.

Обрати внимание на три вещи:

  • Колонки с одинаковыми именами: id. Это id из таблицы employee и id из таблицы task.
  • Строки каждой таблицы повторяются. В левой колонке после ID 6 снова идет ID = 1.
  • У нас есть бессмысленные строки, когда, например id (из таблицы employee) равно 6 и в той же строке employee_id равно 1.

Убираем бессмысленные строки

В нашей результирующей таблице, которая является декартовым произведением всех строк двух таблиц employee и task слишком много строк.

Логично, что если в строке employee_id равно 3, то она должна приклеиваться только к строке из таблицы employee, где id равно 3. Давай попробуем исправить это недоразумение с помощью WHERE.

Напишем такой запрос:

SELECT * FROM employee, task WHERE emploee.id = task.emploee_id 

И результат такого запроса:

id name occupation salary age id emploee_id name deadline
1 Иванов Иван Программист 100000 25 1 1 Исправить багу на фронтенде 2022-06-01
2 Петров Петр Программист 80000 23 2 2 Исправить багу на бэкенде 2022-06-15
4 Рабинович Мойша Директор 200000 35 7 4 Наслаждаться жизнью (NULL)
5 Кириенко Анастасия Офис-менеджер 40000 25 3 5 Купить кофе 2022-07-01
5 Кириенко Анастасия Офис-менеджер 40000 25 4 5 Купить кофе 2022-08-01
5 Кириенко Анастасия Офис-менеджер 40000 25 5 5 Купит кофе 2022-09-01
6 Васька кот 1000 3 8 6 Наслаждаться жизнью (NULL)

Хорошая новость — у нас исчезли бессмысленные строки: id из первой колонки всегда равно employee_id.

Плохая новость — исчезли задачи, которые ни на кого не назначены, такие как уборка офиса. Их employee_id был равен NULL, поэтому они были отброшены после выполнения WHERE.

1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Найти в разных таблицах
Найти в разных таблицах
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Мне приснилось небо Лондона
Мне приснилось небо Лондона
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Когда имя и название равны
Когда имя и название равны
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Разные колонки, разные таблицы
Разные колонки, разные таблицы
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Работа с алиасами таблиц
Работа с алиасами таблиц
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Где же вы красавицы
Где же вы красавицы
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Фильтруем по id
Фильтруем по id
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Найди Халка
Найди Халка
1
Задача
Модуль 4. Работа с БД,  3 уровень,  0 лекция
Недоступна
Сортируем по локации
Сортируем по локации
Комментарии (18)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Underdante
Уровень 74
15 октября, 17:14
это вообще нужно джава разработчику. есть же jdbc и hibernate
Алексей
Уровень 115
16 декабря 2025, 21:02
Ураа. Щелкун вернулся
Artur Chakov
Уровень 112
3 декабря 2025, 08:44
В 302 задаче не правильно стоит в ответе AND customers.location = 'London'. В таблице customers (в файле init_data.sql) нет столбца (поля) location для таблицы customers, есть только adress. Update: в задаче 304 в таблице customers также нет поля location Update 2: в задаче 309 в таблице customers также нет поля location
Виктория
Уровень 50
4 августа 2025, 14:26
"Напишем такой запрос: SELECT * FROM employee, task WHERE emploee.id = task.emploee_id " Я так понимаю должно быть: SELECT * FROM employee, task WHERE employee.id = task.emploee_id (пропустили букву в названии таблицы employee)
Anonymous #izrb#
Уровень 1
3 января 2025, 17:34
А все щелкун на месте
Олег
Уровень 106
Expert
19 сентября 2024, 17:33
Щёлкнул
Олег
Уровень 79
Expert
31 января 2024, 19:50
в 305 задаче разработчики ЗАБЫЛИ В УСЛОВИИ УКАЗАТЬ, что нужно переименовывать с нижним подчеркиванием g_name, c_name и так далее..... в 306 таже шляпа.
Кирилл
Уровень 109
Expert
21 июля 2024, 12:59
Ничего подобного не делал. Переименовывал g.name и c.name и всё прошло успешно.
Yauheni KFull Stack React Java Dev в Google
17 октября 2023, 05:38
поострим - опечатка
AlekseiSieger
Уровень 29
19 июня 2023, 14:52
Может я просто перегорел, но не мог понять, что такое декартово произведение. Когда вы выполняете запрос, который объединяет две таблицы, их строки комбинируются во всех возможных комбинациях. Декартово произведение строк таблицы A и таблицы B означает, что каждая строка из таблицы A будет соединена с каждой строкой из таблицы B.
DanielCEO в BicycleInventionAcad
30 июня 2023, 13:15
Декартово произведение - результат умножения одного множества на другое. При перемножении множеств все члены первого множества умножаются на все члены второго. Т.е. если у нас есть первое множества (а1, б1, с1) и второе множество (а2, б2, с2), то результатом их перемножения будет множество (а1а2, а1б2, а1с2, б1а2, б1б2, б1с2, с1а2, с1б2, с1с2). Можно представить в виде двумерного массива: При этом реальным смысл будет только у диагонали, выделенной зеленым, в то время как все белые клетки будут произвелением несоответствующих друг другу записей. В случае с БД произведение записей это склеивание столбцов в одну строку, поэтому у нас получится вот такая вот выборка: Как видите, бессмысленных полей (белых) здесь больше чем имеющих реальную бизнес-логику(цветных). Это исправляется фильтрацией через равенство первичного ключа первой таблицы и внешнего ключа второй таблицы:
WHERE table1.key = table2.foreign_key
Однако из-за того что сначала делается выборка (обсчитывается все декартово произведение), и только потом оно фильтруется, такой запрос тратит огромное количество ресурсов на обсчет совершенно бессмысленных данных. Поэтому для решения этой задачи используется JOIN, о чем рассказывают далее.
Andrey Sautov
Уровень 108
Expert
16 июля 2023, 13:08
Простыми словами, декартово произведение - это способ "сочетания" элементов из двух множеств для получения всех возможных комбинаций. Как если бы мы взяли два набора предметов и составили все возможные пары, где каждый элемент из первого набора "соединен" с каждым элементом из второго набора. Для примера рассмотрим две таблицы: "Пользователи" (Users) и "Продукты" (Products). Предположим, что у нас есть следующие данные в каждой из таблиц: Таблица "Пользователи": | UserID | Имя | |--------|-----------| | 1 | Анна | | 2 | Петр | | 3 | Мария | Таблица "Продукты": | ProductID | Название | |-----------|-----------| | 101 | Телефон | | 102 | Компьютер | | 103 | Ноутбук | Чтобы получить декартово произведение этих двух таблиц, мы можем выполнить следующий SQL-запрос: ```sql SELECT * FROM Users, Products; ``` Результатом этого запроса будет комбинация каждой строки из таблицы "Пользователи" со всеми строками из таблицы "Продукты": | UserID | Имя | ProductID | Название | |--------|-------|-----------|-----------| | 1 | Анна | 101 | Телефон | | 1 | Анна | 102 | Компьютер | | 1 | Анна | 103 | Ноутбук | | 2 | Петр | 101 | Телефон | | 2 | Петр | 102 | Компьютер | | 2 | Петр | 103 | Ноутбук | | 3 | Мария | 101 | Телефон | | 3 | Мария | 102 | Компьютер | | 3 | Мария | 103 | Ноутбук | Таким образом, мы получаем все возможные комбинации пользователей и продуктов в виде декартова произведения таблиц.
jvatechsBackend Developer в Ищу стажировкуExpert
25 августа 2023, 04:41
Наглядное и простенькое объяснение, респект
Дмитрий
Уровень 117
Expert
13 декабря 2023, 10:46
Это с чего это реальный смысл будет только у диагональных ячеек? Не правда. Реальный смысл будет у тех ячеек, которые соответствуют назначению для каждой строки из таблицы одного из столбцов. Вовсе не обязательно, что они будут по диагонали расположены.
Шахзод
Уровень 1
24 января 2023, 08:17
Судя по итоговой таблице должно быть так:
for (String row2 : table2)
{
  for (String row1 : table1)
   {
    System.out.println(row1 + row2);
   }
}
Потому что берем 1 строку из task (table2) и прикрепляем к её к каждой строке таблицы employee (table1) и так повторяем с каждой следующей строкой task.