Глава 3. Запросы с использованием нескольких таблиц
3.1. О средствах одновременной работы с множеством таблиц

Затрагивая вопросы проектирования баз данных [2], мы выяснили, что базы данных - это множество взаимосвязанных сущностей или отношений (таблиц) в терминологии реляционных СУБД. При проектировании стремятся создавать таблицы, в каждой из которых содержалась бы информация об одном и только об одном типе сущностей. Это облегчает модификацию базы данных и поддержание ее целостности. Но такой подход тяжело усваивается начинающими проектантами, которые пытаются привязать проект к будущим приложениям и так организовать таблицы, чтобы в каждой из них хранилось все необходимое для реализации возможных запросов. Типичен вопрос: как же получить сведения о том, где купить продукты для приготовления того или иного блюда и определить его калорийность и стоимость, если нужные данные "рассыпаны" по семи различным таблицам? Не лучше ли иметь одну большую таблицу, содержащую все сведения базы данных ПАНСИОН ?

Даже при отсутствии средств одновременного доступа ко многим таблицам нежелателен проект, в котором информация о многих типах сущностей перемешана в одной таблице. SQL же обладает великолепным механизмом для одновременной или последовательной обработки данных из нескольких взаимосвязанных таблиц. В нем реализованы возможности "соединять" или "объединять" несколько таблиц и так называемые "вложенные подзапросы". Например, чтобы получить перечень поставщиков продуктов, необходимых для приготовления Сырников, возможен запрос

    SELECT	Продукт, Цена, Название, Статус
    FROM	Продукты, Состав, Блюда, Поставки, Поставщики
    WHERE	Продукты.ПР = Состав.ПР
    AND	Состав.БЛ = Блюда.БЛ
    AND	Поставки.ПР = Состав.ПР
    AND	Поставки.ПС = Поставщики.ПС
    AND	Блюдо = 'Сырники'
    AND	Цена IS NOT NULL;

Продукт | Цена | Название | Статус
--- | --- | --- | ---
Яйца | 1.8 | ПОРТОС | кооператив
Яйца | 2. | КОРЮШКА | кооператив
Сметана | 3.6 | ПОРТОС | кооператив
Сметана | 2.2 | ОГУРЕЧИК | ферма
Творог | 1. | ОГУРЕЧИК | ферма
Мука | 0.5 | УРОЖАЙ | коопторг
Сахар | 0.94 | ТУЛЬСКИЙ | универсам
Сахар | 1. | УРОЖАЙ | коопторг

Он получен следующим образом: СУБД последовательно формирует строки декартова произведения таблиц, перечисленных во фразе FROM, проверяет, удовлетворяют ли данные сформированной строки условиям фразы WHERE, и если удовлетворяют, то включает в ответ на запрос те ее поля, которые перечислены во фразе SELECT.

Следует подчеркнуть, что в SELECT и WHERE (во избежание двусмысленности) ссылки на все (*) или отдельные столбцы могут (а иногда и должны) уточняться именем соответствующей таблицы, например, Поставки.ПС, Поставщики.ПС, Меню.*, Состав.БЛ, Блюда.* и т.п.

Очевидно, что с помощью соединения несложно сформировать запрос на обработку данных из нескольких таблиц. Кроме того, в такой запрос можно включить любые части предложения SELECT, рассмотренные в главе 2 (выражения с использованием функций, группирование с отбором указанных групп и упорядочением полученного результата). Следовательно, соединения позволяют обрабатывать множество взаимосвязанных таблиц как единую таблицу, в которой перемешана информация о многих типах сущностей. Поэтому начинающий проектант базы данных может спокойно создавать маленькие нормализованные таблицы, так как он всегда может получить из них любую "большую" таблицу.

Кроме механизма соединений в SQL есть механизм вложенных подзапросов, позволяющий объединить несколько простых запросов в едином предложении SELECT. Иными словами, вложенный подзапрос - это уже знакомый нам подзапрос (с небольшими огра-ничениями), который вложен в WHERE фразу другого вложенного подзапроса или WHERE фразу основного запроса.

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

    SELECT	Продукт, Цена, Название, Статус
    FROM	Продукты, Состав, Блюда, Поставки, Поставщики
    WHERE	Продукты.ПР = Состав.ПР
    AND	Состав.БЛ = Блюда.БЛ
    AND	Поставки.ПР = Состав.ПР
    AND	Поставки.ПС = Поставщики.ПС
    AND	Блюдо = 'Сырники'
    AND	Цена = (	SELECT	MIN(Цена)
    		FROM	Поставки X
    		WHERE	X.ПР = Поставки.ПР );

Результат запроса имеет вид

Продукт | Цена | Название | Статус
--- | --- | --- | ---
Яйца | 1.8 | ПОРТОС | кооператив
Сахар | 0.94 | ТУЛЬСКИЙ | универсам
Мука | 0.5 | УРОЖАЙ | коопторг
Сметана | 2.2 | ОГУРЕЧИК | ферма
Творог | 1. | ОГУРЕЧИК | ферма

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

2.5.4 | Содержание | 3.2.1