为啥 SQL 强制我在 GROUP BY 子句中重复 SELECT 子句中的所有非聚合字段? [关闭]

Posted

技术标签:

【中文标题】为啥 SQL 强制我在 GROUP BY 子句中重复 SELECT 子句中的所有非聚合字段? [关闭]【英文标题】:Why does SQL force me to repeat all non-aggregated fields from my SELECT clause in my GROUP BY clause? [closed]为什么 SQL 强制我在 GROUP BY 子句中重复 SELECT 子句中的所有非聚合字段? [关闭] 【发布时间】:2010-09-29 19:39:55 【问题描述】:

这个问题困扰我很久了。

99% 的情况下,GROUP BY 子句是 SELECT 子句的精确副本,减去聚合函数(MAX、SUM 等)。 这违反了不要重复自己的原则。

GROUP BY 子句何时不能包含 SELECT 子句减去聚合函数的精确副本?

编辑

我意识到某些实现允许您在 GROUP BY 中拥有与 SELECT 中不同的字段(因此是 99%,而不是 100%),但这肯定是一个非常小的例外吗? 如果您使用不同的字段,有人可以解释应该返回什么吗?

谢谢。

【问题讨论】:

【参考方案1】:

我倾向于同意您的观点 - 这是 SQL 应该具有稍​​微更智能的默认值以节省我们所有输入的许多情况之一。例如,想象一下这是否合法:

Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By *

其中“*”表示“所有非聚合字段”。如果每个人都知道它是如何工作的,那么就不会有混乱了。如果你想做一些棘手的事情,你可以加入一个特定的字段列表,但 splat 的意思是“所有的人”(在这种情况下,这意味着所有可能的)。

当然,“*”在此处的含义与在 SELECT 子句中的含义不同,因此可能使用不同的字符会更好:

Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By !

在其他一些领域,SQL 并没有达到应有的水平。但在这一点上,它可能已经太根深蒂固了,无法做出如此大的改变。

【讨论】:

是的。谢谢。这种事情听起来很明智。我想答案是“没有真正的答案”。 ;) 很难选择有意义的字符或关键字。 * 或“ALL”关键字在语义上不正确,因为您没有按 ALL 分组(您按所有 EXCEPT 分组......)。这不是一个坏主意,但如果没有语义组合,就看不出它是如何工作的。 为什么不只是 GROUP 而不是 GROUP BY 当然,这也可以,但我怀疑这对于当前的 SQL 程序员来说可能不够直观。就此而言,语法也可能是完全不使用它,解析器会知道您应该按任何非聚合字段自动分组。 IMO,这可能一开始就应该这样做,但也许这已经进入了“试图变得如此有帮助,以至于你最终会感到困惑”的领域。【参考方案2】:

因为它们是两个不同的东西,你可以按不在 select 子句中的项目分组

编辑:

另外,做出这样的假设是否安全?

我有一条 SQL 语句

Select ClientName, InvAmt, Sum(PayAmt) as PayTot

服务器假设我想按 ClientName 和 InvoiceAmount 分组是否“正确”? 我个人更喜欢(并且认为它更安全)拥有此代码

Select ClientName, InvAmt, Sum(PayAmt) as PayTot
Group By ClientName

抛出错误,提示我改代码为

Select ClientName, Sum(InvAmt) as InvTot, Sum(PayAmt) as PayTot
Group By ClientName

【讨论】:

确实如此,但是如果 SELECT 列表中的列是强制性的,为什么还需要它们呢? SELECT a, MAX(c) FROM t GROUP BY b 可能意味着按 a 分组,不是吗?我认为这可能只是一个清晰度问题。 隐含操作是调试和测试的祸根。【参考方案3】:

我希望/期待我们很快会看到更全面的内容;有关该主题的 SQL 历史课程将很有用且内容丰富。任何人?任何人?比勒?

与此同时,我可以观察到以下几点:

SQL 早于 DRY 原则,至少就它在 The Pragmatic Programmer 中的记录而言。

并非所有数据库都需要完整列表:例如,Sybase 会很乐意执行类似的查询

SELECT a, b, COUNT(*)
FROM some_table
GROUP BY a

...这(至少每次我不小心运行了这样一个怪物时)经常导致如此庞大的无意记录集,以至于恐慌的请求很快接踵而至,请求 DBA 退回服务器。结果是一种部分笛卡尔积,但我认为这可能主要是 Sybase 未能正确实施 SQL 标准。

【讨论】:

【参考方案4】:

也许我们需要一个简写形式 - 称之为 GroupSelect

GroupSelect Field1, Field2, sum(Field3) From SomeTable Where (X = "3")

这样,如果你遗漏了一个聚合函数,解析器只需要抛出一个错误。

【讨论】:

【参考方案5】:

这样做的充分理由是,如果您没有指定所有列,您将经常得到不正确的结果。假设您有三列,col1col2col3

假设您的数据如下所示:

Col1  Col2 Col3
a      b    1
a      c    1
b      b    2
a      b    3

select col1, col2, sum(col3) from mytable group by col1, col2 将给出以下结果:

Col1  Col2 Col3
a      b    4
a      c    1
b      b    2

它会如何解释select col1, col2, sum(col3) from mytable group by col1

我的猜测是

Col1  Col2 Col3
a      b    5
a      c    5
b      b    2

这些显然是不好的结果。当然,查询越复杂,连接越多,查询返回正确结果或者程序员甚至知道它们是否不正确的可能性就越小。

我个人很高兴group by 需要这些字段。

【讨论】:

mysql——作为支持这一点的 SQL 语法的一个例子——不保证在这种情况下返回什么。实际上,它可以是结果集中的任何随机值。虽然我从未见过这种情况,但它甚至可以从同一关系的不同行返回关系属性。【参考方案6】:

我同意 GROUP BY ALL、GROUP BY * 或类似内容。如原帖所述,在 99%(可能更多)的情况下,您希望按所有非聚合列/表达式进行分组。

但是,出于向后兼容性的原因,这里是一个需要 GROUP BY 列的示例。

SELECT 
  MIN(COUNT(*)) min_same_combination_cnt, 
  MAX(COUNT(*)) max_same_comb_cnt, 
  AVG(COUNT(*)) avg_same_comb_cnt, 
  SUM(COUNT(*)) total_records,
  COUNT(COUNT(*)) distinct_combinations_cnt
FROM <some table>
GROUP BY <list of columns>

这适用于 Oracle。我用它来估计列的选择性。 group by 应用于内部聚合函数。然后,应用外部聚合。

如果能对 SQL 标准的改进提出建议,那就太好了。我只是不知道它是如何工作的。

【讨论】:

【参考方案7】:

实际上,这不是 100% 的时间吗?是否存在您可以在选择中包含不在 GROUP BY 中的(非聚合)列的情况?

虽然我没有答案。对于这门语言来说,这确实是一个尴尬的时刻。

【讨论】:

【参考方案8】:

我同意 op 的观点,即重复有点烦人,特别是如果非聚合字段包含复杂的语句,如 if 和函数以及许多其他内容。如果 group by 子句中可以有一些速记,那就太好了 - 至少是一个列别名。按编号引用列可能是另一种选择,尽管它可能有自己的问题。

【讨论】:

关于长表达式的好点。它确实增加了痛苦。【参考方案9】:

例如,您可能需要从所有分组的行中提取一个 id,以及它们的数量之和。在这种情况下,您将按名称对它们进行分组,而不将 id 分组。 SQLite 似乎以这种方式工作。

【讨论】:

【参考方案10】:

由于 group by 导致整个元组组的单个元组,因此其他非 group by 属性必须仅在聚合函数中使用。如果您在选择中添加非分组属性,则 sql 无法决定从该组中选择哪个值。

【讨论】:

以上是关于为啥 SQL 强制我在 GROUP BY 子句中重复 SELECT 子句中的所有非聚合字段? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

为啥包含 group by、sum 或 where 子句时这条 SQL 语句会挂起?

在 sql 查询中使用聚合函数时避免 group by 子句

SQL语句中,为啥where子句不能使用列别名,而order by却可以?

为啥要在 group by 子句中使用 substr?

为啥没有聚合函数的选择列需要成为 MySQL 中 Group by 子句的一部分?

MYSQL,GROUP BY 子句;这与 sql_mode=only_full_group_by [重复] 不兼容