На главную страницу
Характерные ошибки при решении упражнений.
Задача 23
Моисеенко С.И.
Найдите производителей, которые производили бы как ПК со скоростью не менее 750
МГц, так и ПК-блокноты со скоростью не менее 750 МГц. Вывести: Maker
Эта задача поясняется в FAQ на сайте.
SELECT DISTINCT maker FROM product
WHERE model IN (SELECT model FROM pc WHERE speed>=750)
OR
model IN (SELECT model FROM laptop WHERE speed>=750)
В приведенном решении в результирующий набор попадет также и производитель,
выпускающий что-нибудь одно: либо ПК, либо ПК-блокноты, т.к. для попадания в
результирующий набор достаточно выполнения одного из условий, соединяемых
оператором OR. Что не удовлетворяет условиям задачи и совершенно справедливо
отвергается системой.
Перефразируя Толстого, скажу: "Все правильные решения правильны одинаково, а
неправильные - неправильны по-своему". Вот еще одна попытка "изменить" ситуацию
в лучшую сторону:
SELECT DISTINCT maker FROM product a, pc b, laptop c
WHERE b.speed >= 750 AND c.speed >= 750
AND
(a.model = b.model OR a.model = c.model)
Используя равенство предикатов,
x AND (y OR z) = (x AND y) OR (x AND z)
выполним синтаксические преобразования рассматриваемого запроса:
SELECT DISTINCT maker FROM product a, pc b, laptop c
WHERE ((b.speed >= 750 AND c.speed >= 750)
AND a.model = b.model)
OR
((b.speed >= 750 AND c.speed >= 750)
AND a.model = c.model)
В результирующий набор попадут строки, отвечающие хотя бы одному из предикатов,
соединяемых оператором OR. Рассмотрим, например, запрос с первым предикатом:
SELECT DISTINCT maker FROM product a, pc b, laptop c
WHERE ((b.speed >= 750 AND c.speed >= 750)
AND a.model = b.model)
Перепишем его в синтаксически более удобной форме:
SELECT DISTINCT maker
FROM product a JOIN pc b ON a.model = b.model,
laptop c
WHERE (b.speed >= 750 AND c.speed >= 750)
и далее
SELECT DISTINCT maker FROM
(
SELECT maker FROM product a JOIN pc b ON a.model = b.model
WHERE b.speed >= 750
) x,
(
SELECT * FROM laptop c WHERE (c.speed >= 750)
) y
Теперь, пожалуй, уже можно проанализировать. Первый подзапрос, который мы
обозначили "х" соединяет по внешнему ключу таблицу PC с таблицей Product,
отбирая производителей ПК со скоростью >=750. Второй ("y") отбирает модели
ПК-блокнотов со скоростью >=750.
То, как соединяются "x" и "y", называется декартовым произведением. Т.е.
производитель требуемых ПК будет в результирующем наборе сочетаться с КАЖДОЙ
моделью ПК-блокнота, даже если она произведена ДРУГИМ производителем.
Не знаю, как Толстой, а я, кажется, ошибся. Грабли те же, только в других
кустах, а именно, мы опять получим производителей, которые могут производить
только что-то одно. А "кусты" другие потому, что если НИКТО не производит
ПК-блокноты с требуемой скоростью, то мы получим пустой набор записей. Этот
частично правильный результат не дает запрос из FAQ.
Совпадение результатов на основной базе является совершенно случайным. Так уж
оказалось, что те производители, которые выпускают нужные по условию задачи ПК,
выпускаю также и нужные ПК-блокноты.
Таким образом, несмотря на совпадение результатов на "видимой" базе, запрос не
является правильным при любом совместимом со схемой состоянием БД.
В заключение, чтобы не быть голословным, покажу результаты оригинального запроса
с расширением списка выводимых столбцов. Вот запрос:
SELECT maker, a.model a_m, b.model b_m, c.model c_m
FROM product a, pc b, laptop c
WHERE ((b.speed >= 750 AND c.speed >= 750)
AND a.model = b.model)
OR
((b.speed >= 750 AND c.speed >= 750)
AND a.model = c.model)
А вот пара строк из результата:
Maker |
a_m |
b_m |
c_m |
B |
1121 |
1121 |
1752 |
A |
1752 |
1121 |
1752 |