На главную страницу
Характерные ошибки при решении упражнений.
Задача 41
Моисеенко С.И.
Найдите названия всех тех кораблей из базы данных, о которых можно определенно сказать, что они были спущены на воду до 1941 г.
Еще недавно формулировка этой задачи звучала так:
Найдите названия всех кораблей, спущенных на воду до 1918 г.
Эта задача допускала весьма простое решение:
SELECT name AS shipName
FROM Ships
WHERE launched < 1918
Однако, как справедливо заметил Zhekaus, здесь никак не учитываются даты
сражений. Действительно, если год спуска на воду корабля неизвестен, и при этом
он участвовал в сражении, которое произошло до 1918 года, то такой корабль
следует включать в результат. Поскольку в базе данных нет сражений,
произошедших ранее 1918 года, то приведенное выше решение будет приниматься
системой без учета этого факта.
Чтобы не менять данные, оказалось проще переформулировать задачу, используя
сравнение с другой датой. Теперь требуется найти корабли, спущенные на воду до
1941 года.
Однако есть еще один тонкий момент. Может оказаться, что даже если корабль с
неизвестным годом спуска на воду участвовал в более позднем сражении, его
потребуется включать в результирующий набор.
Почему? Ответ на этот вопрос опять следует искать в "висящих" головных кораблях.
Итак, корабль из Outcomes, имя которого совпадает с именем одного из классов
(головной корабль), отсутствует в таблице Ships или присутствует, но с
неизвестным годом спуска на воду. Пусть в таблице Ships есть корабль того же
класса с известным годом спуска на воду. Если этот год окажется меньше 1941, то
головной корабль следует включать в результирующий набор наряду с упомянутым
кораблем. Это следует из того факта, что головной корабль - это первый корабль
в своем классе и, следовательно, также должен был быть спущен на воду ранее 1941 года.
Есть еще один капкан. Всего лишь год поменять, а сколько интересного можно
извлечь. Попробуйте найти его самостоятельно. В противном случае читаем дальше.
Беда с этими головными кораблями! В описании базы данных сказано, что имя классу
может давать первый корабль в этом классе, который называется головным, или имя
проекта. В последнем случае головного корабля просто не существует. Поэтому
когда в задании сказано найти "все корабли…", нельзя рассматривать таблицу
Classes в качестве списка головных кораблей.
Вот как иногда неправильно решается вопрос о головных кораблях, спущенных на
воду ранее 1941 года:
SELECT class FROM Classes
WHERE EXISTS (SELECT 1 FROM Ships
WHERE launched < 1941 and Ships.class =
Classes.class)
Итак, головным кораблем является корабль, имя которого совпадает с именем
класса. Наконец, о капкане, о котором я говорил выше. Никак не думал, что
поставил капкан на себя. Пока я не написал объяснение, которое вы сейчас
читаете, мне каждый день приходилось отвечать на несколько писем по этому
поводу.
Итак, возможна такая ситуация. Имеется головной корабль с неизвестным годом
спуска на воду. Более того, он может участвовать всего в одном сражении,
например, в 1945 году. Есть другой корабль этого класса, год спуска на воду
тоже неизвестен. Однако если он участвовал в сражении до 1941 года, то нам
следует оба эти корабля включить в результирующий набор, т.к. головной корабль
(если он есть!!!) должен быть спущен на воду ранее любого другого корабля его
класса.
Так что чистая логика, и никаких подвохов.
Приведенные здесь примеры можно выполнить непосредственно на сайте, установив
флажок "Без проверки" на странице с упражнениями
на SELECT.
Перейти к
решению задачи #41
На главную страницу