【问题标题】:Is it really necessary to have GROUP BY in the SQL standardSQL标准中真的有必要有GROUP BY吗
【发布时间】:2018-02-28 15:14:38
【问题描述】:

在写了几年 SQL 之后,我发现我经常把我感兴趣的列放在 SELECT 中,然后在 GROUP BY 中再次指定它们,这很烦人。我不禁想,我们为什么要这样做?

用户必须具体说明分组依据的原因是什么?我们不能让 SQL 引擎假设如果SELECT 中有聚合函数,则按其余非聚合列分组

当您在SELECT 中有一个大的CASE WHEN 时,这将特别有用且更简洁。

【问题讨论】:

  • 当您想要对比 SELECT 中表达的属性更多的属性进行分组时,您仍然需要 GROUP BY。例如,如果你想在 A 和 B 上对 R(A,B,C) 进行分组,然后只选择 B 和 SUM(C),或者类似的东西。
  • 在花了几十年时间为各种风格的 SQL 编写编译器之后,我相当肯定那些编译器作者发现了一些有效的基本原因,可以在他们的每个程序中以几乎相同的方式做同样的事情.我不能说他们是对还是错,但我想很少有人能质疑他们的推理。另外,根据我的经验,允许假设(尤其是在代码中)有时会有点问题。

标签: sql ansi-sql sql-standards


【解决方案1】:

因为它们可能并不总是完全匹配。

例如,如果我想找出每个类别的最大书籍数量,我可以这样做:

select max(cnt)
from (
    select count(*) as cnt
    from books
    group by category
    ) t;

在某些数据库(例如 Oracle)中,您甚至可以这样做:

select max(count(*))
from books
group by category;

我真的不需要指定类别列,因为我不需要它。

Postgres 等少数数据库支持在 group by 子句中使用别名。

【讨论】:

    【解决方案2】:

    我碰巧有点同意你的看法。如果有人想要更深奥的group by——比如说,省略列——那么他们可以使用子查询。

    如果我不得不猜测的话,SQL 的作者并不想在聚合函数中注入如此强大的力量。您的建议意味着select 中的一个函数正在确定结果集中行的定义。通常,select 只是确定列。也就是说,查询失败语法是一回事,因为聚合包含在没有group by 的情况下。 select 中的函数更改正在输出的行是另一回事。

    您可以通过select distinct 使用窗口函数。虽然我不推荐语法,但你可以这样做:

    select distinct x, count(*) over (partition by x)
    from t;
    

    好吧,这消除了group by,但您仍然需要在每个窗口函数中重复分组标准。

    【讨论】:

      【解决方案3】:

      你必须想象你在这里使用了两个版本的表格。例如:

      SELECT …
      FROM table
      GROUP BY …;
      

      首先,请记住SELECT 是在FROMGROUP BY 子句之后评估的。这意味着您可以选择的内容受到这些子句的结果的限制。

      事情是想象GROUP BY 生成一个新的虚拟 表。这个虚拟表只有以下内容:

      • 分组的列
      • 所有列的摘要(聚合)
      • 没有别的

      每个不同组都会有一行摘要。

      如果您想在 SELECT 子句中添加特定列,它必须是组列或摘要,因为您只能从可用的内容中选择 SELECT

      即使没有GROUP BY 子句,也有一个隐含的GROUP BY (),它会产生一行摘要。一些 DBMS(不是全部)甚至允许您添加它,尽管它不会改变任何东西。

      请注意,GROUP BY 摘要中的行数受您要分组的列数的影响。通常,行数类似于 (DISTINCT Group1)*(DISTINCT GROUP2) 等。这意味着您当然不希望分组超过您真正需要的数量。

      例外情况

      假设你有这样的陈述:

      SELECT state, name, count(*)
      FROM customers
      GROUP BY state;
      

      这当然会失败。选择一个状态有多个值的name有什么意义?

      然而,在传统模式下,MySQL 将允许您这样做:它将选择 一个 名称以与状态保持一致。但是,不能保证是哪一个,所以它的价值值得怀疑。

      假设您想按月分组。您可能希望显示月份名称,但按月份编号排序。在这里,您需要按两者分组。使用伪日期函数:

      SELECT monthname, count(*)
      FROM data
      GROUP BY monthname, monthnumber
      ORDER BY monthnumber;
      

      这只不过是一种解决方法。它利用了这样一个事实,即每个月份名称都有一个月份编号,因此没有真正的进一步分组。它只是在GROUP BY 虚拟表中获取两个值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-04-05
        • 2021-01-25
        • 1970-01-01
        • 2013-09-16
        • 2013-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多