Книги и статьи по SQL Rambler's Top100 Switch language to: English 29 октября 2020 г. 4:32:36


www.sql-ex.ru
Skip Navigation Links  

 

Print  Версия для печати

На главную страницу

Характерные ошибки при решении упражнений. Задача 39

Моисеенко С.И.

Найдите корабли, "сохранившиеся для будущих сражений"; т.е. выведенные из строя в одной битве (damaged), они участвовали в другой.

Вот пример неправильно понятого условия:

SELECT DISTINCT ship FROM Outcomes os
    WHERE EXISTS(
        SELECT ship FROM Outcomes oa
        (WHERE oa.ship=os.ship AND result='damaged'
    )
    AND EXISTS(
        SELECT SHIP
         FROM Outcomes ou
        WHERE ou.ship=os.ship
    GROUP BY ship
        HAVING COUNT(battle)>1
    )

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

Ошибка же состоит в том, что проигнорировано условие "сохранившиеся для будущих сражений", которое означает, что после битвы, в которой корабль получил повреждение, он принимал участие в БОЛЕЕ ПОЗДНЕМ сражении. Таким образом, для получения правильного решения этой задачи нужно анализировать даты сражений, которые содержатся в таблице сражений Battles.

Нижеприведенный запрос ранее принимался системой:

SELECT s.name
FROM Ships s JOIN Outcomes o ON s.name = o.ship
   JOIN Battles b ON o.battle = b.name
GROUP BY s.name HAVING COUNT (s.name) = 2
    AND (MIN(result) = 'damaged' or MAX(result) = 'damaged')
UNION
SELECT o.ship
FROM Classes c JOIN Outcomes o ON c.class = o.ship
    JOIN Battles b ON o.battle = b.name
WHERE o.ship NOT IN(SELECT name FROM Ships)
GROUP BY o.ship HAVING COUNT (o.ship) = 2
    AND (MIN(result) = 'damaged' OR MAX(result) = 'damaged')

Во-первых, объединяются запросы, которые выполняют соединение участвующих в сражениях кораблей (таблица Outcomes) с таблицами Ships и Classes соответственно. Кстати говоря, предикат o.ship NOT IN(SELECT name FROM Ships) во втором запросе явно лишний, т.к. UNION исключит возможные дубликаты.

Эти соединения не просто избыточны, они ошибочны, т.к. в описании БД сказано, что в таблице Outcomes могут быть корабли, отсутствующие в Ships. Т.е. если найдется не головной корабль, которого нет в таблице Ships и который отвечает условиям задачи, то он не попадет в результирующий набор вышеприведенного запроса.

Во-вторых, предикат HAVING COUNT (o.ship) = 2 ограничивает возможные варианты только двумя сражениями корабля. А почему корабль не может принимать участие более чем в двух сражениях? Он же не обязательно был потоплен после того, как получил повреждение.

В третьих, мне не вполне понятно условие:

(min(result) = 'damaged' or max(result) = 'damaged')

В соответствии с описанием предметной области корабль может быть:

Damaged (поврежден)
Ok (невредим)
Sunk (потоплен)

Поэтому условие min(result) = 'damaged' будет выполнено, если в одной из битв корабль был поврежден (при естественной сортировке текстовых строк буква "D" идет раньше, чем буквы "O" и "S"). Однако это совсем не означает, что поврежден он был прежде, чем принял участие в следующем сражении, что требуется по условиям задачи. Здесь уже нужно оценивать дату сражения. Что же касается max(result) = 'damaged', то это условие не будет выполняться, если результаты сражений были разные; а если одинаковые, то это не даст ничего нового по сравнению с первым условием на минимум.

Вот такое наложение ошибок давало правильный результат на обеих базах. Меры были приняты, т.е. добавлены проверочные данные, на которых данное решение дает неверный результат.

Приведенные здесь примеры можно выполнить непосредственно на сайте, установив флажок "Без проверки" на странице с упражнениями на SELECT.

Перейти к решению задачи #39

На главную страницу

Print  Версия для печати


Использование любых материалов данного сайта возможно только
при условии обязательного размещения прямой ссылки на сайт
http://www.sqlbooks.ru
на каждой странице, где размещены используемые материалы.

 Начало   Статьи    Книги 
Рейтинг@Mail.ru Rambler's Top100 Alt Упражнения по SQL: обучение, тестирование, сертификация по языку SQL Copyright c 2002-2006. All rights reserved.