SQL - 查询 - max(count())
Posted
技术标签:
【中文标题】SQL - 查询 - max(count())【英文标题】:SQL - Query - max(count()) 【发布时间】:2012-12-26 11:53:14 【问题描述】:我正在准备我的数据库系统考试(明天),但在要求我编写查询的练习中遇到了麻烦。这是一个例子:
我被要求写一个查询来回答以下问题:在年龄最小的作者中,谁写的书最多?
问题是我的老师禁止我在 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
(请注意,Sean 没有写任何书,第 6 本书有 2 个作者,而 Rick 是拥有最多书的作者 (4))
现在,我上面写的代码给出了这个结果(我猜):
AName | NBooks
-----------------
Lena | 3
John | 2
(最低年龄是25岁,莉娜和约翰都是25岁)
问的是:
AName | NBooks
-----------------
Lena | 3
(Lena 是作者,在所有作者中年龄最小(25 岁),着书最多)
提前致谢
【问题讨论】:
你是指年龄最低的作者吗? 两个问题:和使用 TOP 可能意味着它像内联视图一样被禁止,或者必须使用它来代替内联视图。它是哪一个?也允许使用 Row_number 吗? 是的,最低岁,抱歉。 请不要使用隐式连接语法(逗号分隔的FROM
子句),明确指定连接会更清楚。我不明白为什么你的老师让你写这样的查询,因为JOIN
中的子查询可能会比有这些限制的东西表现得更好。尤其是“仅单值”版本...如果列名上有每个表的前缀(尤其是单个字符 - 如果您突然需要一位艺术家来写一本书,会发生什么情况?) .为什么人们坚持Age
之类的东西?
对不起,这是我学习的方式,也是我将被评估的方式。我只是遵循评估规则。
【参考方案1】:
因为你是学生,所以我会回答部分问题。这是一个答案,忽略了最年轻的部分:
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
子句中的 ,
。再一次,如果你的老师没有教现代语法(大约从 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
s,隐式执行了两个join
s(年龄子句),以及两个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
子句中的子查询就像在进行三足赛跑。是的,你可能可以到达那里,但你会用两条腿跑得更快。
【讨论】:
非常感谢!对limit
和top
的限制适用于整个课程。我也不允许在项目中使用它。我不记得有任何理由,我只记得老师说“如果你使用top
,你得到0;如果你在for
中使用select
并且它有效,你最多得到一半”
如果你想学习 SQL,你可以看看我的书《使用 SQL 和 Excel 进行数据分析》。很抱歉你有这么差的教练。
上限和下限的限制似乎是一项严厉的措施。再说一次,只要他给你可能的任务,这可能是一个好处。
这两个查询不等价。第一个查询可以有多个结果,而第二个查询可以只有一个。
@GordonLinoff 是真的,但 OP 做了clarify it。无论如何,值得注意的是差异,因为在现实生活场景中,通常倾向于首先处理关系。如果开发人员在没有考虑并与最终用户讨论的情况下选择了一个,他们会邀请未来的错误报告。【参考方案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
的。
【讨论】:
虽然解决这个问题对我来说也是一场个人战斗,但我喜欢 Gordon 的回答,因为他只给出了部分答案……毫无疑问,他在我之前解决了这个问题。 你能解释一下这条线的需要吗(AND [a3].[AName] <> [a1].[AName]
)?我没听懂。
好电话,您可以忽略它。我首先没有在有子句中的“或等于”部分。然后我不得不排除作者自己的计数才能得到结果。【参考方案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
【讨论】:
感谢您的帮助,但您在FROM
中有一个 SELECT
。我不允许这样做。【参考方案4】:
如果您被允许使用 CTE 并对其进行排名。
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
【讨论】:
不是那么简单。如果在我给出的例子中,约翰写了另一本书,他也写了 3 本书,就像莉娜一样,所以结果一定是这两本书。抱歉,我从未听说过 CTE。 @MarcoCastanho 好点。我将其更改为使用 RANK 而不是 ROW_NUMBER 而 CTE 实际上非常非常接近是一个子查询......这违反了主要限制之一。 @Jacco 你知道你也有一个子查询(实际上是三个)。虽然我同意 CTE 与内联视图非常相似 @ConradFrix True,但限制是“不要在 FROM 子句中使用子查询”。我的在 WHERE 和 HAVING 子句中。【参考方案5】:如果您只想要一个结果,请选择前一个结果,其余的由排序完成。我个人会做一个排名函数来使用 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
【讨论】:
谢谢,但这也不回答。以上是关于SQL - 查询 - max(count())的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Oracle SQL 中使用 MAX() 和 COUNT()?