如何在此查询中选择Max?帮助考试

时间:2012-02-26 05:44:25

标签: sql aggregate-functions greatest-n-per-group

所以,我将通过很多练习进行最后的SQL考试,我在星期四进行了最后一次SQL考试,我遇到了另一个疑问。

练习中的表格应该来自酒店数据库。你有三个表:

    STAY               ROOM                  ROOM_TYPE
    ===========        ============          ============
PK  ID_STAY        PK  ID_ROOM           PK  ID_ROOM_TYPE
    DAYS_QUANT         ID_ROOM_TYPE  FK      DESCRIPTION
    DATE               PRICE
    ID_ROOM  FK

他们要求我做的查询是“按房间类型显示2011年租用最多天数(总计)的房间的所有数据(您必须显示房间类型)和描述)“

这是我解决它的方式,我不知道是否可以:

  SELECT RT.ID_ROOM_TYPE, RT.DESCRIPTON, R.*, SUM(S.DAYS_QUANT)
    FROM STAY S, ROOM R, ROOM_TYPE RT
   WHERE YEAR(S.DATE) = '2011'
GROUP BY RT.ID_ROOM_TYPE, RT.DESCRIPTON, R.*
ORDER BY SUM(S.DAYS_QUANT) DESC
   LIMIT 1

所以,我不确定的第一件事就是R. *我包括在内。我可以把它放在SELECT中吗?是否也可以像GROUP BY一样包含它?

另一件事我不确定我是否可以在考试中使用LIMIT或SELECT TOP 1语句。谁能想到一种解决这个问题的方法而不使用它们?喜欢MAX()语句还是什么?

5 个答案:

答案 0 :(得分:1)

select r.*, t.*
  from room r
  join room_type t on t.id_room_type = r.id_room_type
 where r.id in
       (select
               (select r.id_room
                  from room r
                  join stay on stay.id_room = r.id_room
                 where year(s.date) = '2011'
                   and r.id_room_type = t.id_room_type
                 group by r.id_room
                 order by sum(s.days_quant) desc
                 limit 1) room_id
          from room_type t)

答案 1 :(得分:1)

始终可以避免LIMIT 1或SELECT TOP。一种方法是将顶行表示为没有较高行的行。什么不存在表达“没有。”的想法。

考虑到这一点的一种方法如下:选择那些没有相同类型的房间且总天数更多的房间(连同它们的总天数和类型信息)。这给你这个查询(不仔细校对):

with StayTotals as (
  select
    STAY.ID_ROOM, 
    ROOM_TYPE.ID_ROOM_TYPE,
    ROOM_TYPE.DESCRIPTION,
    SUM(STAY.DAYS_QUANT) AS TotalDays2011
  from STAY join ROOM on STAY.ID_ROOM = ROOM.ID_ROOM
            join ROOM_TYPE on ROOM.ID_ROOM_TYPE = ROOM_TYPE.ID_ROOM_TYPE
  where YEAR(STAY.DATE) = 2011
  group by STAY.ID_ROOM, ROOM_TYPE.ID_ROOM_TYPE, ROOM_TYPE.DESCRIPTION
)
  select *
  from StayTotals as T1
  where not exists (
    select *
    from StayTotals as T2
    where T2.ID_ROOM_TYPE = T1.ID_ROOM_TYPE
    and T2.TotalDays2011 > T1.TotalDays2011
  );

如果你不能使用CTE(WITH子句),你可以使用子查询重写它,但它很尴尬。

排名函数已经成为SQL标准的一部分了很长一段时间。如果你可以使用它们,这也可能有效:

with StayTotals as (
  select
    STAY.ID_ROOM, 
    ROOM_TYPE.ID_ROOM_TYPE,
    ROOM_TYPE.DESCRIPTION,
    SUM(STAY.DAYS_QUANT) AS TotalDays2011
  from STAY join ROOM on STAY.ID_ROOM = ROOM.ID_ROOM
            join ROOM_TYPE on ROOM.ID_ROOM_TYPE = ROOM_TYPE.ID_ROOM_TYPE
  where YEAR(STAY.DATE) = 2011
  group by STAY.ID_ROOM, ROOM_TYPE.ID_ROOM_TYPE, ROOM_TYPE.DESCRIPTION
), StayTotalsRankedByType as (
  select
    ID_ROOM, 
    ID_ROOM_TYPE,
    DESCRIPTION,
    TotalDays2011,
    RANK() OVER (
      PARTITION BY ID_ROOM_TYPE
      ORDER BY TotalDays2011 DESC
    ) as RankInRoomType
  from StayTotals
)
  select
    ID_ROOM, 
    ID_ROOM_TYPE,
    DESCRIPTION,
    TotalDays2011
  from StayTotalsRankedByType
  where RankInRoomType = 1;

最后,另外一种拉入额外列来描述分组MAX结果的方法是使用“carryalong”排序,这在排名函数可用之前是一种方便的技术。 Adam Machanic给出了一个示例here,来自Usenet的主题有一些有用的主题,例如this one

答案 2 :(得分:1)

我相信你不能使用CTE,因此我扩展了Steve Kass的答案的最后部分。您可以通过比较一个房间被占用的总天数与任何相同类型的房间被占用的最大总天数来获得没有TOP或限制条款的预期结果。为此,您首先按房间总计天数,然后使用相同的派生表,每个房型最多可获得天数。按房间类型和天数加入两个房间,您将隔离最常用的房间。然后您加入起始表以显示所有数据。与TOP或Limit不同,如果出现平局,这将产生更多记录。

P.S。这是没有测试过的。我相信它会奏效,但可能会有拼写错误。

select r.*, rt.*, roomDays.TotalDays
from Room r inner join Room_type rt
   on r.id_room_type = rt.id_room_type
   inner join 
      (select id_room, id_room_type, sum(days_quant) TotalDays
      from Stay
         inner join Room
           on Stay.id_room = Room.id_room
      where year(Date) = 2011
      group by id_room, id_room_type) roomDays
   on r.id_room = roomDays.id_room
   inner join 
      (select id_room_type, max(TotalDays) TotalDays
      from 
         (select id_room, id_room_type, sum(days_quant) TotalDays
          from Stay
             inner join Room
               on Stay.id_room = Room.id_room
          where year(Date) = 2011
          group by id_room, id_room_type) roomDaysHelper
      group by id_room_type) roomTypeDays
   on r.id_room_type = roomTypeDays.id_room_type
   and roomDays.TotalDays = roomTypeDays.TotalDays

答案 3 :(得分:0)

这个怎么样?

select room.id_room, room_type.description, room.price
from room inner join room_type 
on room.id_room.type = room_type.id_room_type
where room.room_id = (select room_id from stay
where year (date) = 2011
group by id_room
order by sum (days_quant) desc);

不幸的是,这个查询(现在是这样)并没有显示多天来最受欢迎的房间被租用了。但是没有'限制1'!

答案 4 :(得分:0)

谢谢大家!有了你给我的所有想法,我想出了这个,请告诉我你是否认为没问题!

SELECT R.ID_ROOM, R.ID_ROOM_TYPE, T.DESCRIPTION, SUM(S.DAYS_CUANT)
FROM ROOM R, ROOM_TYPE T, STAY S
(SELECT ID_STAY, SUM(S.DAYS_QUANT) TOTALDAYS
 FROM STAY S
 WHERE YEAR(S.DATE) = 2011
 GROUP BY S.ID_STAY) STAYHELPER
WHERE YEAR(S.DATE) = 2011 
GROUP BY R.ID_ROOM, R.ID_ROOM_TYPE, T.DESCRIPTION
HAVING SUM(S.DAYS_QUANT) >= ALL STAYHELPER.TOTALDAYS