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
                      )

我想你可以弄清楚如何修改它。

顺便说一句,我希望对limittop 的限制仅适用于这个示例。否则,你应该找另一位老师,因为这些都是非常重要的结构。

此外,您需要学习传统的连接语法,而不是 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 和一个排序。我的查询的完整版本显式执行了两个joins,隐式执行了两个joins(年龄子句),以及两个group bys。前者会比后者有更好的表现。

从可读性的角度来看,这个版本更短更简洁。我还认为教它在做什么比第一个版本中的“不寻常”构造要容易得多。大多数学生会理解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

这也比回答问题的查询更有效。不能使用topfrom 子句中的子查询就像在进行三足赛跑。是的,你可能可以到达那里,但你会用两条腿跑得更快。

【讨论】:

非常感谢!对limittop 的限制适用于整个课程。我也不允许在项目中使用它。我不记得有任何理由,我只记得老师说“如果你使用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] &lt;&gt; [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())的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 函数 ,子查询 和原生SQL查询

SQL分组查询

如何在 Oracle SQL 中使用 MAX() 和 COUNT()?

LINQ to SQL语句之Select/Distinct和Count/Sum/Min/Max/Avg (转)

高级SQL查询-(聚合查询,分组查询,联合查询)

Mysql优化方案