【问题标题】:Portable GROUP BY clause rules?可移植的 GROUP BY 子句规则?
【发布时间】:2020-01-26 20:27:03
【问题描述】:

我不经常使用GROUP BY 子句,但我想我对它的使用规则有一个大致的了解。 我也不会使用各种各样的数据库。

我刚刚对使 GROUP BY 子句“可移植”的规则感兴趣。

我根据一些实验列出了一份清单,但我不太确定它是否全面或正确。 希望有经验的大侠能进来看看。


可移植 GROUP BY 子句的规则

这些规则存在 GROUP BY 子句,

  • 如果使用聚合表达式并且不存在显式 GROUP BY 子句,则隐含具有空分组集的 GROUP BY 子句
  • SELECT/HAVING/ORDER BY 子句中的非聚合表达式中使用的列必须在 GROUP BY 子句中
  • SELECT 子句中的别名表达式不得在 GROUP BY 子句中使用
  • GROUP BY 子句可能包含不在 SELECT 子句中的列
  • GROUP BY 子句在 HAVING 之前是必需的

对于这些规则,GROUP BY 子句可能存在也可能不存在,

  • 任何列可以在 SELECT/HAVING/ORDER BY 子句的聚合表达式中使用
  • ORDER BY 子句可能引用 SELECT 子句中的别名聚合表达式
  • HAVING 子句不得在 SELECT 子句中引用别名

以下是一些详细说明,

聚合与非聚合表达式

聚合函数是 SUM()AVG()MAX() 等函数。 它们从多行中获取值,并将它们聚合为单个值。

聚合表达式聚合来自多行的值。

非聚合表达式每行产生一个值。


别名与非别名表达式

别名表达式具有以下形式,expr AS alias

无别名表达式只是没有AS alias 部分的表达式。

在 SELECT 子句中使用别名表达式


如果使用聚合表达式并且不存在显式 GROUP BY 子句,则隐含具有空分组集的 GROUP BY 子句

以下查询是等价的,

  • SELECT SUM(myColumn) FROM myTable;
  • SELECT SUM(myColumn) FROM myTable GROUP BY ();

DB Fiddle

PostgreSQL DB Fiddle

SELECT/HAVING/ORDER BY 子句中的非聚合表达式中使用的列必须在 GROUP BY 子句中

有效查询示例,

  • SELECT myColumn FROM myTable GROUP BY myColumn;
  • SELECT myColumn + 1 FROM myTable GROUP BY myColumn;
  • SELECT myColumn FROM myTable GROUP BY myColumn, otherColumn ORDER BY otherColumn ASC;
  • SELECT myColumn FROM myTable GROUP BY myColumn, otherColumn ORDER BY otherColumn + 1 ASC;
  • SELECT myColumn FROM myTable GROUP BY myColumn, otherColumn HAVING otherColumn >= 2;
  • SELECT myColumn FROM myTable GROUP BY myColumn, otherColumn HAVING otherColumn + 1 >= 2;

DB Fiddle

无效查询示例,

  • SELECT myColumn FROM myTable GROUP BY otherColumn;
  • SELECT myColumn FROM myTable GROUP BY myColumn ORDER BY otherColumn ASC;
  • SELECT myColumn FROM myTable GROUP BY myColumn HAVING otherColumn >= 2;

DB Fiddle


SELECT 子句中的别名表达式不得在 GROUP BY 子句中使用

无效查询示例,

  • SELECT myColumn AS x FROM myTable GROUP BY x;
  • SELECT myColumn+otherColumn AS x FROM myTable GROUP BY x;
  • SELECT SUM(myColumn) AS x FROM myTable GROUP BY x;
  • SELECT myColumn+otherColumn AS x FROM myTable GROUP BY x;

MS SQL Server - SQL Fiddle

有效查询示例,

  • SELECT myColumn AS x FROM myTable GROUP BY myColumn;
  • SELECT myColumn+otherColumn AS x FROM myTable GROUP BY myColumn+otherColumn;

MS SQL Server - SQL Fiddle


GROUP BY 子句可能包含不在 SELECT 子句中的列

有效查询示例,

  • SELECT myColumn FROM myTable GROUP BY myColumn, otherColumn;
  • SELECT myColumn FROM myTable GROUP BY myColumn, otherColumn, myTableId;

DB Fiddle


GROUP BY 子句在 HAVING 之前是必需的

有效查询示例,

  • SELECT myColumn FROM myTable GROUP BY myColumn HAVING myColumn >= 2;
  • SELECT myColumn FROM myTable GROUP BY myColumn HAVING SUM(otherColumn) > 3;

DB Fiddle

无效查询示例,

  • SELECT 1 FROM myTable HAVING myColumn >= 2;
  • SELECT 1 FROM myTable HAVING SUM(otherColumn) > 3;

DB Fiddle


任何列可以在 SELECT/HAVING/ORDER BY 子句的聚合表达式中使用

有效查询示例,

  • SELECT SUM(myColumn+otherColumn) FROM myTable;
  • SELECT SUM(myColumn+otherColumn) FROM myTable GROUP BY otherColumn;
  • SELECT SUM(myColumn+otherColumn) FROM myTable ORDER BY SUM(myColumn+otherColumn) DESC;
  • SELECT SUM(myColumn+otherColumn) FROM myTable GROUP BY otherColumn ORDER BY SUM(myColumn+otherColumn) DESC;
  • SELECT SUM(myColumn+otherColumn) FROM myTable GROUP BY otherColumn HAVING SUM(myColumn+otherColumn) >= 3;

DB Fiddle


ORDER BY 子句可能在 SELECT 子句中引用别名聚合表达式

有效查询示例,

  • SELECT SUM(myColumn) AS x FROM myTable ORDER BY x DESC;
  • SELECT SUM(myColumn) AS x FROM myTable GROUP BY otherColumn ORDER BY x DESC;

DB Fiddle


HAVING 子句不得在 SELECT 子句中引用别名

无效查询示例,

  • SELECT myColumn AS x FROM myTable GROUP BY myColumn HAVING x >= 2;

DB Fiddle


我在 DB Fiddle 上试验了 MySQL、PostgreSQL 和 SQLite。

我想知道其他数据库是否禁止某些内容,但我认为这是允许的,因为我只玩过这 3 个。

或者也许(几乎)所有数据库都允许某些东西,但我认为这是被禁止的,因为脑子放屁。

【问题讨论】:

  • 你的问题到底是什么?
  • "SELECT/HAVING/ORDER BY 子句中非聚合表达式中使用的列必须在 GROUP BY 子句中" -- 不一定。如果一列“直接”依赖于您分组的列,则不需要聚合它。
  • “任何列都可以在 SELECT/HAVING/ORDER BY 子句的聚合表达式中使用”——不是这样。一些数据库对列施加限制。例如,不能使用 BLOB 或 CLOB 等大值。此外,一些数据库最多将散列截断为 255 个字符。
  • “ORDER BY 子句可能在 SELECT 子句中引用别名聚合表达式”——并非所有数据库都支持这一点。为安全起见,请使用原始列名。
  • “如果使用聚合表达式并且不存在显式的 GROUP BY 子句,则隐含具有空分组集的 GROUP BY 子句”——您需要澄清这一点。如果未指定 GROUP BY 子句,则必须聚合所有列。

标签: sql


【解决方案1】:

group by 是 SQL 标准的一部分。虽然 SQL 数据库经常偏离标准,但最好了解标准,而不是“SQL 是 $database 所做的”。 group by 已在数据库中很好地标准化,但会有一些怪癖。

不幸的是,SQL 标准非常昂贵,并且经过了多次修订。幸运的是,即使是标准的旧副本仍然有用。特别是具有语法和句法规则的第 2 部分。我建议你找到一份副本并通读一遍。

此外还有这些免费资源。


让我们将您的一些派生规则与标准对齐。我正在使用 SQL:1999,因为这就是我所拥有的。

你的。

如果使用聚合表达式并且不存在显式 GROUP BY 子句,则隐含具有空分组集的 GROUP BY 子句

SQL:1999.

6.16.2 COUNT(*) 的参数和 <general set function> 的参数源是第 7.10 条<having clause> 和第 7.11 条<query specification> 中指定的表或分组表的组。


你的。

在 HAVING 之前需要 GROUP BY 子句。

这是不正确的。 group by clause 必须在 having clause 之前,但两者都不是必需的。

SQL:1999.

7.4

<table expression> ::=
   <from clause>
   [ <where clause> ]
   [ <group by clause> ]
   [ <having clause> ]

SQL-99 Complete,真的指出you can have a having clause without a group by clause

SELECT COUNT(a) FROM Somethings
HAVING COUNT(a) > 5;

这个 SQL 语句的意思是“如果整个 Table 中的 a 超过 3 个,则显示有多少 a”。和往常一样,因为在 SELECT 语句中有一个集合函数,所以有一个隐含的 GROUP BY()。因此分组规则适用:此类 SQL 语句中的选择列表可能仅包含单值列。


你的。

SELECT/HAVING/ORDER BY 子句中的非聚合表达式中使用的列必须在 GROUP BY 子句中

SQL-99 完整,真的。

您必须遵循“The Single-Value Rule” - 选择列表中命名的每个列也必须是一个分组列,除非它是一组函数之一的参数。


你的。

SELECT 子句中的别名表达式不得在 GROUP BY 子句中使用。

GROUP BY 子句可能包含不在 SELECT 子句中的列。

SQL-99 完整,真的。

GROUP BY 子句中的每个列都必须明确命名一个列,该列属于 SELECT 语句的 FROM 子句中命名的表。该名称可能是合格的,即:它可能是一个 .这样的 Column 称为分组 Column:它的值将被分组以获得最终结果。

SELECT a*5 AS b FROM T GROUP BY b; 非法:a 不是分组列,并且在选择列表出现之前不评估 b;到时候就晚了

SELECT a+5 FROM T GROUP BY a+5; 非法:GROUP BY 中的表达式

你的。

ORDER BY 子句可以在 SELECT 子句中引用别名聚合表达式。

SQL-99 完整,真的。

GROUP BY 子句和 ORDER BY 子句表面上的相似,往往会误导人们。最大的区别在于分组是在输入(即 FROM 子句中命名的表)上完成的,而排序是在输出上完成的(即选择列表中命名的列)。所以,虽然你可以说“ORDER BY integer”(虽然仅在 SQL-92 中)和“ORDER BY expression”,但说“GROUP BY integer”或“GROUP BY expression”是没有意义的。另一方面,分组列不必在选择列表中,而排序的列必须。

你的。

HAVING 子句不能引用 SELECT 子句中的别名

SQL-99 完整,真的。

HAVING 子句中的操作数受到与选择列表中相同的限制:

  • 两者中的列表达式必须是每个组的单值。
  • 列引用必须明确。
  • 如果 SELECT 语句包含 HAVING 而没有前面的 GROUP BY 子句,则选择列表不能包含对属于 FROM 子句中命名的表的列的任何引用,除非这些引用与 set 函数一起使用。
  • 如果 HAVING 包含子查询,则它不能包含外部 Column 引用,除非这些引用用于对 Column 进行分组或与 set 函数一起使用。

等等。

【讨论】:

  • 您的“单值规则”可能不完整。还有第三种情况,当列“直接依赖”于分组列时,例如,如果您按 PK 分组,那么您可以以非聚合形式键入表的任何其他列。
  • @TheImpaler 有没有按主键分组有用的情况?也就是select ... group by idselect ...有区别的情况吗?
  • "group by 子句必须在 having 子句之前,但两者都不是必需的。" ——我也一直这么认为。可以使用没有 group by 子句的 having 子句;但是SQLite threw me for a loop。而且我对跨大量数据库(包括 SQLite)可移植的查询感兴趣。
  • "SELECT a+5 FROM T GROUP BY a+5; 非法:GROUP BY 中的表达式"——这让我很感兴趣。我测试了 PostgreSQL、MySQL、SQLite 和 MSSQL,它们都允许这样做。我想知道从那时起标准是否已经改变,或者他们是否都决定添加这个扩展。我认为我从未在 GROUP BY 中使用过表达式,但我认为它是允许的。也许我应该试着拿到一份副本……
  • @JustinAnyhowStep 是的,在group by 中使用表达式和列别名是一个非常明显的扩展。查看the Postgres SELECT docs 的兼容性部分。一般来说,Postgres 会记录它们与标准的偏差。在最终资源上,Hyperpolyglot。我鼓励您以类似的方式记录您对 SQL 扩展和偏差的发现。
猜你喜欢
  • 2016-04-06
  • 2012-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-23
  • 1970-01-01
  • 2011-09-25
  • 2019-07-29
相关资源
最近更新 更多