我正在攻读我的数据库系统考试(明天),我在练习中遇到麻烦,我被要求编写查询。这是一个例子:
我被要求写一个查询来回答以下问题:年龄最低的作者中,谁写的书最多?
问题是 我的老师禁止我在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
(注意肖恩没有写任何书,第6本书有2位作者,而里克是大多数书籍的作者(4))
现在,我上面写的代码给出了这个结果(我猜):
AName | NBooks
-----------------
Lena | 3
John | 2
(最低年龄为25岁,Lena和John均为25岁)
问的是:
AName | NBooks
-----------------
Lena | 3
(Lena是作者,所有年龄最小的作者(25),大多数书籍都写过)
提前致谢
答案 0 :(得分: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
)
我认为你可以弄清楚如何修改它。
顺便说一句,我希望对limit
和top
的限制仅适用于此示例。否则,你应该得到另一位老师,因为这些是非常重要的结构。
此外,您需要学习传统的连接语法,而不是,
子句中的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
(age子句)和两个group by
s。前者的表现要好于后者。
从可读性的角度来看,这个版本更短更干净。我也认为教这是做什么要容易得多,而不是第一版中的“不寻常”结构。大多数学生会理解top
和order 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
这比回答问题的查询更有效。无法在top
子句中使用from
或子查询就像是在进行三条腿竞赛。是的,你可以到达那里,但是你可以用自己的两条腿跑得更快。
答案 1 :(得分: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
。
答案 2 :(得分: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
答案 3 :(得分:1)
据我所知,你只想要一行;
您可以先限制作者,然后使用内部联接,您可以从Write表中检索他的姓名和书籍数。
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
答案 4 :(得分:1)
如果你被允许使用CTE和RANK它的繁琐。
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