Упражнение 46 (подсказки и решения)

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

-- Корабли, участвующие в битве при Гвадалканале и которые есть в Ships
-- Обратите внимание на использование коррелирующего подзапроса в
-- предложении WHERE, который решает проблему устранения дубликатов при
-- декартовом произведения
SELECT a.ship, displacement, numGuns
FROM (SELECT ship
      FROM Outcomes
      WHERE battle = 'Guadalcanal'
      ) AS a, Classes
WHERE class IN (SELECT class
                FROM Ships
                WHERE name = a.ship
                )
UNION
-- Аналогичный по логике запрос, который выбирает те головные корабли из
-- Outcomes, которые сражались при Гвадалканале.
SELECT a.ship, displacement, numGuns
FROM (SELECT ship
      FROM Outcomes
      WHERE battle = 'Guadalcanal'
      ) AS a, Classes
WHERE class IN (SELECT ship
                FROM Outcomes
                WHERE ship = a.ship
                )
UNION
--По сути, это решение 3.1.1
SELECT a.ship, displacement, numGuns
FROM (SELECT ship
      FROM Outcomes
      WHERE battle = 'Guadalcanal'
     ) AS a 
    LEFT JOIN Classes ON a.ship = class;
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]

В результате получим лишние строки, характерным примером которых являются такие:

shipdisplacementnumGuns
California3200012
CaliforniaNULLNULL

Можно еще утяжелить этот запрос (и сделать его менее эффективным), добавив код для исключения ошибочной строки. Критерием здесь может служить присутствие NULL-значения, например, в столбце displacement, если есть другая строка с тем же именем корабля. Однако мы советуем обойтись без этого и решить задачу иначе. Тем более, что это возможно, в чем легко убедиться, зайдя на форум сайта, посвященный этой задаче.

В заключение приведем почти правильное решение:

SELECT name, displacement, numGuns
FROM Classes, Ships
WHERE Classes.class = Ships.class 
AND name IN (SELECT Ship
             FROM Outcomes
             WHERE battle = 'Guadalcanal'
            )
UNION
SELECT class, displacement, numGuns
FROM Classes
WHERE class IN(SELECT ship
               FROM Outcomes
               WHERE battle = 'Guadalcanal'
              );
mssql
🚫
[[ error ]]
[[ column ]]
[[ value ]]

Первый запрос из объединения в этом решении находит информацию о кораблях, которые есть в таблице Ships и которые принимали участие в сражение при Гвадалканале.

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

Так что же здесь неверно? Если до сих пор непонятно, вернитесь к обсуждению задачи.

Решить задачу на SQL-EX.RU