为什么不能在单个SELECT中混合聚合值和非聚合值?

时间:2011-05-07 08:55:25

标签: sql aggregate-functions

我知道如果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更容易实现?如果有人能向我解释为什么它不能像我认为的那样起作用,我将非常感激。

6 个答案:

答案 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,如何折叠记录