Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
all_lab.doc
Скачиваний:
56
Добавлен:
14.11.2019
Размер:
1.42 Mб
Скачать

7. Вложенные запросы в MySql

Вложенный запрос позволяет использовать результат, возвращаемый одним запросом, в другом запросе. Возможность применения одного запроса внутри другого и была причиной появления слова «структурированный» в названии «структурированный язык запросов» (SQL). Так как результат возвращает только оператор SELECT, то в качестве «вложенного» запроса всегда выступает SELECT-запрос. В качестве внешнего запроса может выступать запрос с участием любого SQL-оператора: SELECT, INSERT, UPDATE, DELETE и др.

Замечание: Термин «вложенный запрос» имеет различные варианты в русскоязычной литературе, такого рода запросы часто называют подчиненными запросами и подзапросами.

Замечание: Вложенные запросы появились в СУБД MySQL, начиная с версии 4.1

Рассмотрим учебную базу данных, которая является базой компьютерного магазина, торгующего комплектующими (скрипт test.sql).

Пусть требуется вывести название товарных позиций из таблицы products для раздела «Процессоры» таблицы catalogs. Решить эту задачу можно, например, при помощи двух SQL-запросов.

Первый запрос извлекает первичный ключ (id_catalogs) записи из таблицы catalogs, соответствующей каталогу «Процессоры».

SELECT @id:=id_catalog FROM `catalogs` WHERE name='Процессоры'

Второй запрос выводит названия товарных позиций и их цены для данного каталога.

SELECT name, price FROM `products`

WHERE id_catalog = @id

ORDER BY price

name

price

Celeron 1.8

  1 595

Celeron D 315 2.26GHz

  1 880

Celeron D 320 2.4GHz

  1 962

Celeron 2.0GHz

  1 969

Celeron 2.4GHz

  2 109

Celeron D 325 2.53GHz

  2 747

Intel Pentium 4 3.0GHz

  5 673

Intel Pentium 4 3.0GHz

  6 147

Intel Pentium 4 3.2GHz

  7 259

Как видно из примера, промежуточное значение помещается в переменную @id. Вложенные запросы позволяют осуществить подобную выборку в одном запросе.

SELECT name, price FROM `products`

WHERE id_catalog = (SELECT @id:=id_catalog FROM `catalogs` WHERE name='Процессоры')

ORDER BY price;

Таким образом, результат вложенного запроса (id_catalog), минуя промежуточные переменные в MySQL или внешней программы, применяется в качестве элемента внешнего запроса. Вложенный запрос всегда помещается в круглые скобки.

Получить аналогичный результат можно при помощи многотабличного запроса:

SELECT `products`.name, `products`.price FROM `products`, `catalogs`

WHERE `catalogs`.id_catalog = `products`.`id_catalog` AND

`catalogs`.name='Процессоры'

ORDER BY price

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

Вложенный запрос может применяться не только с условием WHERE, но также в конструкциях DISTINCT, GROUP BY, ORDER BY, LIMIT, JOIN, UNION и др.

Вложенный запрос как скалярный операнд

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

Пусть требуется определить название каталога, в котором присутствует самая дорогая товарная позиция

SELECT name FROM `catalogs`

WHERE id_catalog = (SELECT id_catalog FROM `products`

WHERE price = (SELECT MAX(price)

FROM `products`))

name

Процессоры

Как видно из примера, степень вложенности запросов не ограничивается вторым уровнем, могут использоваться трёхуровневые и многоуровневые вложенные запросы. Первый внутренний запрос определяет максимальную цену в таблице products:

SELECT MAX(price) FROM `products`

Внешний по отношению к нему запрос определяет значение первичного ключа (id_catalog) для товарной позиции с максимальным значением цены (7259.00):

SELECT id_catalog FROM `products` WHERE price = 7259.00

Полученное значение поля id_catalog (равное 1) подставляется во внешний запрос:

SELECT name FROM `catalogs` WHERE id_catalog = 1

Замечание: На практике редко прибегают к вложенным запросам со степенью вложенности больше трёх, так как большая степень вложенности быстро приводит к увеличению времени выполнения запроса.

Вложенные запросы, возвращающие несколько строк

Если вложенный запрос возвращает несколько строк, СУБД MySQL возвращает ошибку №1242 – вложенный запрос возвращает более чем одну строку.

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

SELECT name FROM `catalogs`

WHERE id_catalog IN (SELECT id_catalog FROM `products`

GROUP BY id_catalog)

name

Процессоры

Материнские платы

Видеоадаптеры

Жёсткие диски

Оперативная память

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

Конструкция IN позволяет осуществить поиск величины в списке и выражает логику операции отношения «=». Однако на месте этой операции может стоять другой операция отношения. Для этого используется ключевое слово ANY

Например,

SELECT name FROM `catalogs`

WHERE id_catalog > ANY (SELECT id_catalog FROM `products`

GROUP BY id_catalog)

name

Материнские платы

Видеоадаптеры

Жёсткие диски

Оперативная память

Вместо ключевого слова ANY может быть использовано ключевое слово ALL, которое точно так же используется совместно с одним из шести операций отношения («=», «<>», «<», «<=», «>», «>=»).

Например,

SELECT name, price FROM `products`

WHERE price > ALL (SELECT AVG(price) FROM `products`

GROUP BY id_catalog);

name

price

Intel Pentium 4 3.2GHz

  7 259

Intel Pentium 4 3.0GHz

  6 147

Intel Pentium 4 3.0GHz

  5 673

Asustek P4C800-E Delux

  5 395

ASUSTEK A9600XT/TD

  5 156

GIGABYTE AGP GV-N59X128D

  5 886

Для того чтобы понять логику запроса полезно выполнить вложенный запрос отдельно:

SELECT AVG(price) FROM `products` GROUP BY id_catalog

AVG(price)

3482.333333

2801.166667

3843.500000

2749.000000

1417.000000

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

Проверка на существование

Результирующая таблица, которая возвращается вложенным запросом, может быть пустой, т.е. не содержать ни одной строки. Для проверки данного факта используются ключевые слова EXISTS и NOT EXISTS. Данные ключевые слова не требуют левого операнда, они просто сообщают, имеются ли в результирующей таблице строки или нет. Если вложенный запрос возвращает более одной строки, EXISTS возвращает 1 (истина), в противном случае ключевое слово возвращает 0 (ложь). Ключевое слово NOT EXISTS действует противоположным образом.

Замечание: Проверка на существование допустима только во вложенных запросах.

Например, пусть требуется вывести названия товарных позиций из таблицы products, которые были выбраны покупателями, т.е. для которых имеется отметка о покупке в таблице orders.

SELECT id_product, name, price FROM `products`

WHERE EXISTS (SELECT * FROM `orders`

WHERE `orders`.`id_product`=`products`.`id_product`)

id_product

name

price

  8

Intel Pentium 4 3.0GHz

  6 147

  10

Gigabyte GA-8I848P-RS

  1 896

  20

Maxtor 6Y120P0

  2 456

Внешний запрос последовательно перебирает все строки таблицы products и для каждой товарной позиции выполняет вложенный запрос, который проверяет, имеется ли в таблице orders текущая товарная позиция. Число записей, которые возвращает вложенный запрос, может быть более одной.

Подзапросы в конструкции FROM

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

SELECT usr.surname, usr.name, orders.`ordertime`

FROM `orders`,

(SELECT * FROM `users`) AS usr

WHERE `orders`.`id_user`=usr.id_user;

surname

name

ordertime

Симдянов

Игорь

04.01.2005 10:39

Корнеев

Александр

10.02.2005 9:40

Иванов

Александр

18.02.2005 13:41

Симдянов

Игорь

10.03.2005 18:20

Симдянов

Игорь

17.03.2005 19:15

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

Задания

Вариант 1

Создать БД, состоящую из таблиц АВТОР (Фамилия, имя, отчество, пол, дата рождения, телефон) и КНИГА (Название, цена, тематика, издательство, тираж). Дополнить первичными и внешними ключами, уточнить структуру и связать таблицы.

Заполнить таблицы данными – 5-6 записей.

Реализовать следующие запросы:

  1. Определить автора самой дорогой книги.

  2. Определить авторов, не печатающих свои книги в издательстве «АСТ».

  3. Определить авторов, написавших наибольшее количество книг.

  4. Удалить информацию об авторах, не имееющих телефона.

  5. Увеличить на 50% цену книг, вышедших в издательстве «АСТ».

  6. Удалить сведения об авторах, чьи произведения не издаются.

Вариант 2

Создать БД, состоящую из таблиц РЕЙС (№ рейса, конечный пункт, дата вылета, продолжительность маршрута) и БИЛЕТ (Номер места, дата продажи, фамилия пассажира). Дополнить первичными и внешними ключами, уточнить структуру и связать таблицы. Заполнить таблицы данными – 5-6 записей.

Реализовать следующие запросы:

  1. Определить список пассажиров, покупающих билеты на самые дальние рейсы.

  2. Вывести список пассажиров, не летающих в Самару.

  3. Определить, кто из пассажиров потратил наибольшую сумму на покупку авиабилетов.

  4. Удалить данные о билетах, проданных за прошлый месяц.

  5. Увеличить на 10% стоимость билетов на рейсы 23-45 и 56-78.

  6. Удалить информацию о билетах, ошибочно проданных после вылета самолета.

Вариант 3

Создать БД, состоящую из таблиц БЛЮДО (Название, время приготовления, повар, стоимость_блюда) и КОМПОНЕНТ (Название, калорийность, вес, стоимость 100 гр). Дополнить первичными и внешними ключами, уточнить структуру и связать таблицы в предположении, что один определенный компонент принимает участие только в одном блюде.

Заполнить таблицы данными – 5-6 записей.

Реализовать следующие запросы:

  1. Определить блюдо, которое можно приготовить быстрее всех остальных блюд.

  2. Определить, кто из поваров не готовит десерт.

  3. Определить самое калорийное блюдо.

  4. Удалить сведения о блюдах, для которых не указаны имя повара или время приготовления.

  5. Установить калорийность 100 ккал, для компонентов, у которых она не указана.

  6. Удалить сведения о блюдах, стоимость которых меньше средней стоимости их компонентов.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]