2013-01-11 161 views
15

我正在爲我的數據庫系統考試(明天)進行學習,並且在練習中遇到問題時需要編寫查詢。這裏有一個例子:SQL - Query - max(count())

enter image description here

我被要求寫一個查詢,以回答以下問題:在與最低年齡的作家,誰最書籍寫的?

問題是我的老師不允許我使用子查詢中FROM子句和使用TOP

我寫了一個答案,但一個我知道是不正確的:

SELECT W.AName, COUNT(W.ID_B) AS NBooks 
FROM Write W, Author A1 
WHERE (A1.AName = W.AName) AND 
     (A1.AAge = (SELECT MIN(A2.Age) 
        FROM Author A2)) 
GROUP BY W.AName 
ORDER BY NBooks DESC 

這一次給出了低年齡的所有作者,以及他們各自的寫的書(我希望..)號。正確的答案應該只是這一個的第一行。

我要明確指出:

Table Author 
AName | AAge 
--------------- 
John  | 25 
Rick  | 30 
Sean  | 26 
Lena  | 25 

Table Writes 
AName | ID_B 
--------------- 
John  | 2 
Lena  | 1 
John  | 3 
Lena  | 4 
Rick  | 5 
Rick  | 6 
Lena  | 6 
Rick  | 7 
Rick  | 8 

(請注意,肖恩我以前不寫任何一本書,書nº6有2個作家,和Rick是筆者與大多數書籍(4))

現在我上面寫的代碼給出了這樣的結果(我猜):

AName | NBooks 
----------------- 
Lena  | 3 
John  | 2 

(最低年齡爲25歲,並都莉娜和約翰是25)

請告訴我問的是:

AName | NBooks 
----------------- 
Lena  | 3 

(莉娜是作者,都用最低年齡(25),以書面大多數書籍的作者之間)

在此先感謝

+2

您的意思是*最低*年齡的作者? – dotjoe

+0

有兩個問題:*和使用TOP *可能意味着它的內聯視圖是禁止的,或者它必須用來代替內聯視圖。這是什麼?還使用Row_number是否允許? –

+0

是的,與_lowest_年齡,對不起。 –

回答

1

如果你只是想其中一個結果是選擇一個頂部,剩下的就是排序。我個人會做一個排名函數,以明確使用Aggregate()Over()窗口函數獲得排名。但是,既然你正在學習,也許他們不想把它提出來,並告訴你'頂部'是如何工作的。

declare @Person Table (personID int identity, person varchar(8), age int); 

insert into @Person values ('Brett', 34),('John', 34),('Peter', 52); 

declare @Books Table (BookID int identity, personID int); 

insert into @Books values (1),(1),(1),(2),(2),(3) 

Select top 1 -- TOP WILL LIMIT TO CHOICE YOU WANT BASED ON ORDER BY CLAUSE 
    p.person 
, p.age 
, count(b.BookID) as cnts 
from @Person p, @Books b 
where p.personID = b.personID 
group by p.person, p.age 
order by age, cnts desc 
+0

謝謝,但那也沒有答案。 –

+0

我剛剛用一個例子編輯了我原來的帖子。 –

+0

我剛剛編輯了我的示例:) – djangojazz

1

我知道你只是想要1行作爲結果;

您可以先限制作者,然後通過使用內部聯接,您可以從寫入表中檢索他的姓名和書籍數量。

SELECT W.AName, COUNT(W.ID_B) AS NBooks 
FROM Write W INNER JOIN Author A1 ON A1.AName = W.AName 
WHERE 
A1.AName = (SELECT AName FROM Write GROUP BY AName ORDER BY COUNT(ID_B) DESC) 
AND A1.AAge = (SELECT MIN(A2.Age) FROM Author A2) 
GROUP BY W.AName 
ORDER BY NBooks DESC 
+0

感謝您的幫助,但您在'FROM'內有'SELECT'。我不能這樣做。 –

1

如果你被允許使用CTE和RANK其trival。

WITH cte 
    AS (SELECT a.aname, 
       A.aage, 
       Count(id_b)        Book_Count, 
       RANK() 
        OVER( 
        ORDER BY a.aage, Count(id_b) DESC) rn 
     FROM author a 
       INNER JOIN writes w 
         ON a.aname = w.aname 
     GROUP BY a.aname, 
        a.aage) 
SELECT aname, 
     Book_Count 
FROM cte 
WHERE rn = 1 

SQL Fiddle

Demo Where John writes another book

+0

不是那麼簡單。如果在我給出的例子中,約翰寫了另一本書,他也會寫出三本書,比如說莉娜,所以結果必然會有兩本。我從來沒有聽說過CTE,對不起。 –

+0

@MarcoCastanho好點。我將它改爲使用RANK而不是ROW_NUMBER –

+0

而CTE實際上非常非常非常近乎是子查詢......這違反了主要限制之一。 – Jacco

5

因爲你是一個學生,我會回答這個問題的一部分。這是一個答案,忽略最年輕的部分:

select a.AName, COUNT(*) as NumBooks 
from Author a join 
    Write w 
    on a.AName = w.AName 
group by a.AName 
having count(*) >= all(select COUNT(*) as NumBooks 
         from write w 
         group by w.AName 
        ) 

我想你可以找出如何修改它。

順便說一句,對limittop的限制是,我希望只對這個例子。否則,你應該找另一位老師,因爲這些都是非常重要的構造。

此外,您需要了解常規連接語法,而不是from子句中的,。再一次,如果你的老師沒有教授現代的sytnax(自1988年以來),找一個新的老師。而且,我假設子查詢的限制也適用於CTE。

我也想指出查詢的「正確」的版本:

select top 1 a.aname, count(*) as NumBooks 
from Author a join 
    Write w 
    on a.AName = w.AName 
group by author.name, author.Aage 
order by author.Age asc, count(*) desc 

該查詢比沿幾乎任何尺寸上面的查詢更好。它有一個join,一個group by和一種。我的查詢的完整版本明確地執行兩個join,兩個join隱式(年齡子句)和兩個group by s。前者將比後者有更好的表現。

從可讀性的角度來看,這個版本更短,更乾淨。我還認爲,教這個做的事情要容易得多,而不是第一個版本中的「異常」構造。大多數學生會理解toporder by正在做什麼並且可以模擬這一點。模仿having條款中發生的事情需要一些心理體操。

如果你想獲得所有與最大計數的作者,第一件事就是要認識到以前的查詢等效於:

select aname, NumBooks 
from (select a.aname, count(*) as NumBooks, 
      row_number() over (partition by author.Name order by a.aAge, count(*) desc) as seqnum 
     from Author a join 
      Write w 
      on a.AName = w.AName 
     group by author.name, author.Aage 
    ) aw 
where seqnum = 1 

切換這讓所有的作者也很容易:

select aname, NumBooks 
from (select a.aname, count(*) as NumBooks, 
      dense_rank() over (partition by author.Name order by a.aAge, count(*) desc) as seqnum 
     from Author a join 
      Write w 
      on a.AName = w.AName 
     group by author.name, author.Aage 
    ) aw 
where seqnum = 1 

這比回答問題的查詢更有效。不能在from子句中使用top或子查詢就像運行三條腿的比賽。是的,你大概可以到達那裏,但是你可以通過自己的兩條腿快速跑到那裏。

+0

非常感謝! 對「限制」和「頂部」的限制適用於整個過程。我也沒有被允許在項目中使用它。我不記得任何理由,我只記得老師說:「如果你使用'top',你會得到0;如果你在'for'中使用'select'並且它可以工作,那麼你至多得到一半」 –

+1

如果你想學習SQL,你可以看看我的書「使用SQL和Excel進行數據分析」。對不起,你有這樣一個可憐的老師。 –

+0

限制頂部和限制似乎是一個嚴厲的措施。再次,只要他給你可能的任務,這可能是一個好處。 – yumaikas

2

這是一些限制,但它使一個使用它的創造力。

所以,你要最年輕的作者之一,已經寫了一些書籍,高於(或等於)的另一個最年輕的作家寫的書任何其他數...

SELECT 
    [a1].[AName], 
    [a1].[AAge], 
    COUNT(*) AS [NBooks] 
FROM [Author] [a1], [Writes] [w1] 
WHERE 
    [a1].[AName] = [w1].[AName] 
    AND [a1].[AAge] = (SELECT MIN([a2].[AAge]) FROM [Author] [a2]) 
GROUP BY 
    [a1].[AName], 
    [a1].[AAge] 
HAVING COUNT(*) >= ALL 
    (SELECT 
    COUNT(*) AS [NBooks] 
    FROM [Author] [a3], [Writes] [w2] 
    WHERE 
    [a3].[AName] = [w2].[AName] 
    AND [a3].[AAge] = (SELECT MIN([a4].[AAge]) FROM [Author] [a4]) 
    AND [a3].[AName] <> [a1].[AName] 
    GROUP BY 
    [a3].[AName], 
    [a3].[AAge]) 

PS:不得不承認,我從Gordon Linoff瞭解到ALL

+1

你不妨給我[小提琴吧](http://sqlfiddle.com/#!6/78dd9/78) –

+1

雖然解決這個問題也是我個人的一場戰鬥,但我喜歡戈登的回答,只給部分答案......毫無疑問,他在我之前解決了這個問題。 – Jacco

+0

您能否解釋一下這條線的需求('AND [a3]。[AName] <> [a1]。[AName]')?我沒有得到它。 –