我知道如果SELECT语句中有一个聚合函数,那么语句中的所有其他值必须是聚合函数,或者在GROUP BY子句中列出。我不明白为什么就是这种情况。
如果我这样做:
SELECT Name, 'Jones' AS Surname FROM People
我明白了:
NAME SURNAME
Dave Jones
Susan Jones
Amy Jones
因此,DBMS从每一行中取一个值,并在结果集中为其添加一个值。没关系。但如果可行的话,为什么我不能这样做:
SELECT Name, COUNT(Name) AS Surname FROM People
看起来是相同的想法,从每一行获取一个值并附加一个值。但不是:
NAME SURNAME
Dave 3
Susan 3
Amy 3
我明白了:
您尝试执行的查询不包含指定表达式“ContactName”作为聚合函数的一部分。
我知道这是不允许的,但这两种情况看起来很相似,我不明白为什么。是否使DBMS更容易实现?如果有人能向我解释为什么它不能像我认为的那样起作用,我将非常感激。
答案 0 :(得分:17)
聚合对完整结果不起作用,它们只对结果中的组起作用。
考虑一个包含以下内容的表:
Person Pet
-------- --------
Amy Cat
Amy Dog
Amy Canary
Dave Dog
Susan Snake
Susan Spider
如果您使用在Person上分组的查询,它会将数据划分为以下组:
Amy:
Amy Cat
Amy Dog
Amy Canary
Dave:
Dave Dog
Susan:
Susan Snake
Susan Spider
如果您使用聚合,例如计数聚合,它将为每个组生成一个结果:
Amy:
Amy Cat
Amy Dog
Amy Canary count(*) = 3
Dave:
Dave Dog count(*) = 1
Susan:
Susan Snake
Susan Spider count(*) = 2
因此,查询select Person, count(*) from People group by Person
为您提供了每个组的一条记录:
Amy 3
Dave 1
Susan 2
如果您尝试在结果中获取Pet字段,那么这不起作用,因为每个组中该字段可能有多个值。
(有些数据库,比如MySQL,确实允许这样做,只返回组内的任何随机值,你有责任知道结果是否合理。)
如果您使用聚合,但未指定任何分组,则仍会对查询进行分组,整个结果为单个组。因此,查询select count(*) from Person
将创建包含所有记录的单个组,并且聚合可以计算该组中的记录。结果包含每个组中的一行,并且由于只有一个组,因此结果中将有一行。
答案 1 :(得分:8)
这样考虑一下:当你在没有分组的情况下调用COUNT时,它会将表“折叠”到一个组中,从而无法访问select子句中组内的各个项目。
您仍然可以使用子查询或交叉联接获取结果:
SELECT p1.Name, COUNT(p2.Name) AS Surname FROM People p1 CROSS JOIN People p2 GROUP BY p1.Name
SELECT Name, (SELECT COUNT(Name) FROM People) AS Surname FROM People
答案 2 :(得分:5)
正如其他人所解释的那样,当你有一个GROUP BY
或者你在COUNT()
列表中使用SELECT
之类的聚合函数时,你正在进行一组行,因此会折叠匹配的行每个小组分成一个。
当您仅在SELECT
列表中使用汇总函数而没有GROUP BY
时,请将其视为GROUP BY 1
,因此所有行都会分组,折叠为一行。所以,如果你有一百行,那么数据库就不能真正显示你的名字,因为有一百个。
但是,对于具有“窗口”功能的RDBMS,您想要的是可行的。例如。使用没有GROUP BY
的聚合函数。
SQL-Server的示例,其中计算表中的所有行(名称):
SELECT Name
, COUNT(*) OVER() AS cnt
FROM People
以上如何运作?
它显示Name
之类的
COUNT(*) OVER() AS cnt
没有
存在和
它显示COUNT(*)
喜欢它是否正在进行总分组
表
另一个例子。如果表格上有Surname
字段,您可以使用此类字段显示按姓氏分组的所有行,并计算有多少人具有相同的姓氏:
SELECT Name
, Surname
, COUNT(*) OVER(PARTITION BY Surname) AS cnt
FROM People
答案 3 :(得分:2)
您的查询会隐式请求结果集中的不同类型的行,并且不允许这样做。返回的所有行应该是相同类型并且具有相同类型的列。
'SELECT name,surname'想要为表中的每一行返回一行。
'SELECT COUNT(*)'想要返回组合表中所有行结果的单行。
我认为你是正确的,在这种情况下,数据库可以合理地执行两个查询,然后将'SELECT COUNT(*)'的结果复制到每个结果中。不这样做的一个原因是它将是一个隐形的性能影响:你实际上是在做一个额外的自我加入,而不是在任何地方声明它。
其他答案已经解释了如何编写此查询的工作版本,因此我不会深入研究。
答案 4 :(得分:1)
聚合函数和group by子句不是单独的东西,它们是出现在查询中不同位置的同一事物的一部分。如果您希望在列上聚合,则必须说出要用于聚合的函数;如果你希望有一个聚合函数,它必须应用于某些列。
答案 5 :(得分:1)
聚合函数从具有特定条件的多行中获取值,并将它们组合成一个值。此条件由您的语句中的GROUP BY
定义。因此,如果没有GROUP BY
使用
SELECT Name, 'Jones' AS Surname FROM People
您只需选择具有固定值的其他列...但使用
SELECT Name, COUNT(Name) AS Surname FROM People GROUP BY Name
您告诉DBMS选择名称,记住表格中每个名称出现的频率并将它们折叠成一行。因此,如果省略DBMS无法分辨的GROUP BY
,如何折叠记录