sql-ex.ru select statement exercise 51

2019-09-03 12:02发布

问题:

I'm following the exercises in http://www.sql-ex.ru/learn_exercises.php.

In exercise 51 they ask to "Find the names of the ships with the largest number of guns among all ships having the same displacement (including ships in the Outcomes table)."

This is my code, it passes the 1st seen db, but doesn't pass the 2nd, unseen, db (the error is - "less by 3"):

with t1 as
(select displacement, max(numguns) 'guns' from classes group by displacement),
t2 as 
(select class from classes, t1 where classes.displacement = t1.displacement and classes.numguns = t1.guns)
select name from ships, t2 where ships.class = t2.class
union
select ship from outcomes, t2 where outcomes.ship = t2.class

The logic I use is that the only table in the db holding displacement and num of guns is the Classes, and so t1 and t2 extracts the class-names and then I just get the names belonging to these classes from the ships and outcomes table.

Can't see why there should be any error.

EDIT: Added a check for null values in the classes, and I get a new error on the 2nd unseen db - "data mismatch (11)":

with t1 as
(select displacement, max(numguns) 'guns' from classes group by displacement),
t2 as 
(select class from classes, t1 where (classes.displacement = t1.displacement or classes.displacement is null) and (classes.numguns = t1.guns or classes.numguns is null))
select name from ships, t2 where ships.class = t2.class
union
select ship from outcomes, t2 where outcomes.ship = t2.class

回答1:

If you will look into database description of relevant exercise you will see this :

Ships in classes all have the same general design. A class is normally assigned either the name of the first ship built according to the corresponding design, or a name that is different from any ship name in the database. The ship whose name is assigned to a class is called a lead ship.

That's means that outcomes table may contains the lead ship whose name is used as a class in table classes. That is why you need union the name and class from ship with name and name again from outcomes due to lead ship have same class as name.

so, you can use this query or similar with same logic behind:

WITH    cte
          AS ( SELECT   A.name ,
                        C.numGuns ,
                        C.displacement
               FROM     ( SELECT    S.name ,
                                    S.class
                          FROM      dbo.Ships AS S
                          UNION
                          SELECT    O.ship ,
                                    O.ship
                          FROM      dbo.Outcomes AS O
                        ) AS A
                        JOIN dbo.Classes AS C ON A.class = C.class
             )
    SELECT  cte.name
    FROM    cte
            JOIN ( SELECT   cte.displacement ,
                            MAX(cte.numGuns) AS MaxNumGun
                   FROM     cte
                   GROUP BY cte.displacement
                 ) AS M ON cte.displacement = M.displacement
                           AND cte.numguns = M.MaxNumGun

output