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

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

Модуль 4. Работа с БД
3 уровень , 0 лекция
Открыта

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

Теперь немного усложним наши запросы. Добавим в нашу базу данных новую таблицу 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 лекция
Недоступна
task0301
Выбрать все колонки (используй *) из таблиц gyms и customers.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0302
Требуется найти все записи из таблиц gyms и customers, где location равно London. Используй WHERE.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0303
Требуется найти все записи из таблиц gyms и customers, где name из таблицы gyms равно name из таблицы customers. Используй WHERE.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0304
Требуется найти колонки name и location из таблицы gyms, и колонки sex и location из таблицы customers.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0305
Требуется найти колонки id и name из таблицы gyms, и колонки name и email из таблицы customers, но при этом заменив название таблицы gyms на 'g', а название таблицы customers на 'c'. Используй AS для временной замены названий.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0306
Требуется найти колонку location из таблицы gyms, и колонки name и sex из таблицы customers, но при этом заменив название таблицы gyms на 'gym', а название таблицы customers на 'visitor'. Используй AS для временной замены названий.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0307
Требуется найти колонки id и name из таблицы gyms, и колонку id из таблицы customers, но при этом заменив название таблицы gyms на 'gym', а название таблицы customers на 'cust'. Также, нам не нужны все id из таблицы cust, а только те, которые меньше 50. Используй AS и WHERE.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0308
Требуется найти колонку location из таблицы gyms, и колонки name, email и telephone из таблицы customers, но при этом заменив название таблицы gyms на 'g', а название таблицы customers на 'person'. Также, нам нужен person только с именем 'Hulk'. Используй AS и WHERE.
1
Задача
Модуль 4. Работа с БД, 3 уровень, 0 лекция
Недоступна
task0309
Первым делом требуется выбрать колонки location из таблиц gyms и customers, но заменив при этом название таблицы gyms на 'gym', а название таблицы customers на 'person'. Также, нам нужно исключить из результата location 'London' таблицы person. И в конце концов следует сгрупи
Комментарии (17)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Алексей Уровень 79
16 декабря 2025
Ураа. Щелкун вернулся
Artur Chakov Уровень 87
3 декабря 2025
В 302 задаче не правильно стоит в ответе AND customers.location = 'London'. В таблице customers (в файле init_data.sql) нет столбца (поля) location для таблицы customers, есть только adress. Update: в задаче 304 в таблице customers также нет поля location Update 2: в задаче 309 в таблице customers также нет поля location
Виктория Уровень 49
4 августа 2025
"Напишем такой запрос: 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
А все щелкун на месте
Олег Уровень 106 Expert
19 сентября 2024
Щёлкнул
Олег Уровень 79 Expert
31 января 2024
в 305 задаче разработчики ЗАБЫЛИ В УСЛОВИИ УКАЗАТЬ, что нужно переименовывать с нижним подчеркиванием g_name, c_name и так далее..... в 306 таже шляпа.
Кирилл Уровень 109 Expert
21 июля 2024
Ничего подобного не делал. Переименовывал g.name и c.name и всё прошло успешно.
Yauheni K Уровень 1
17 октября 2023
поострим - опечатка
AlekseiSieger Уровень 29
19 июня 2023
Может я просто перегорел, но не мог понять, что такое декартово произведение. Когда вы выполняете запрос, который объединяет две таблицы, их строки комбинируются во всех возможных комбинациях. Декартово произведение строк таблицы A и таблицы B означает, что каждая строка из таблицы A будет соединена с каждой строкой из таблицы B.
Daniel Уровень 51
30 июня 2023
Декартово произведение - результат умножения одного множества на другое. При перемножении множеств все члены первого множества умножаются на все члены второго. Т.е. если у нас есть первое множества (а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
Простыми словами, декартово произведение - это способ "сочетания" элементов из двух множеств для получения всех возможных комбинаций. Как если бы мы взяли два набора предметов и составили все возможные пары, где каждый элемент из первого набора "соединен" с каждым элементом из второго набора. Для примера рассмотрим две таблицы: "Пользователи" (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 | Ноутбук | Таким образом, мы получаем все возможные комбинации пользователей и продуктов в виде декартова произведения таблиц.
jvatechs Уровень 111 Expert
25 августа 2023
Наглядное и простенькое объяснение, респект
Дмитрий Уровень 117 Expert
13 декабря 2023
Это с чего это реальный смысл будет только у диагональных ячеек? Не правда. Реальный смысл будет у тех ячеек, которые соответствуют назначению для каждой строки из таблицы одного из столбцов. Вовсе не обязательно, что они будут по диагонали расположены.
Шахзод Уровень 1
24 января 2023
Судя по итоговой таблице должно быть так:

for (String row2 : table2)
{
  for (String row1 : table1)
   {
    System.out.println(row1 + row2);
   }
}
Потому что берем 1 строку из task (table2) и прикрепляем к её к каждой строке таблицы employee (table1) и так повторяем с каждой следующей строкой task.
Владимир Уровень 109 Expert
29 ноября 2022
id — уникальный номер задания (и стоки в таблице); Товаищи, здесь не хватает одной буквы! Для этого в SQL можно выполоть запрос кто у нас тут был плохим заплосиком? 🤣