2012-02-26 77 views
0

所以,我要通過很多練習來進行最後一次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()語句或類似的東西?

+1

'LIMIT'不是ANSI,只支持MySQL,PostgreSQL和SQLite。 'FETCH FIRST#ROWS'是ANSI,但直到最近...... – 2012-02-26 05:52:18

+0

@OMG小馬這麼說......教授在考試中不會讓我們這麼做,因爲它只是最近才被添加到ANSI中......我想我最好找到另一種方法來做這個查詢 – nachoargentina 2012-02-26 06:00:41

回答

1

我相信你不允許使用CTE,所以我擴展了Steve Kass的答案的最後部分。您可以通過比較一個房間佔用的總天數與任何相同類型的房間被佔用的最大總天數進行比較,在沒有TOP或Limit條款的情況下得到期望的結果。要做到這一點,您首先需要每天總計一次,然後使用相同的衍生表格獲取每個房間類型的最長天數。加入按房間類型和日期你會隔離大多數使用的房間。然後你加入起始表來顯示所有的數據。與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 
+0

謝謝您的查詢!您通過在FROM子句中創建一個「幫助表」來給我一個關於如何實現這一點的好主意。 我把它作爲答案發布,如果你可以給它看,並且告訴我你是否認爲沒問題,我會重新討論它! – nachoargentina 2012-02-26 20:59:26

+0

富有想象力,但不幸的是它不起作用。 ALL需要子查詢。除此之外,你可能需要在派生表中使用id_room而不是id_stay。順便說一下,我注意到你不會在你的代碼中加入連接。你的sql引擎是否執行某種自然連接,或者爲了簡潔而忽略它們? – 2012-02-26 23:55:08

+0

是的,爲了簡潔,我省略了它們。 TX! – nachoargentina 2012-02-28 01:42:07

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) 
+0

偉大的方式來做到這一點,但這種方式也使用「限制1」。您認爲沒有「限制」或「選擇最上面的」聲明,有一種簡單的方法可以做到這一點? – nachoargentina 2012-02-26 06:36:41

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'!

+0

我不認爲這會奏效。子查詢(從...中選擇room_id)通常會返回多個值。 (也沒有列名room_id) – 2012-02-26 18:12:24

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 
); 

如果您不能使用熱膨脹係數(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

+0

哇史蒂夫,這真是太棒了!我真的很滿意你在這一項上的努力。不幸的是,我不認爲我可以在考試中使用CTE,因爲他們沒有教我們,也不是該計劃的一部分。但是,你給了我一個關於如何用子查詢來做的好主意。我會盡量從中做出一些事情,然後自己發表一個答案。如果您稍後可以看一看並告訴我您的想法,我將非常感激。再次男人! – nachoargentina 2012-02-26 20:08:53

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