SQL 标准中是否真的需要 GROUP BY
Is it really necessary to have GROUP BY in the SQL standard
在写了几年SQL之后,我发现不得不把我感兴趣的栏目放在SELECT
中,然后在GROUP BY
中再次指定它们,这常常很烦人。我不禁想,为什么我们必须这样做?
用户必须具体说明分组依据的列的原因是什么?我们不能让 SQL 引擎假设 如果 SELECT
中有聚合函数,按其余非聚合列分组 ?
当您在 SELECT
中有一个大的 CASE WHEN
时,这将特别有用并且更加简洁。
因为它们可能并不总是完全匹配。
例如,如果我想找出每个类别的最大图书数量,我可以这样做:
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 子句中使用别名。
我碰巧有点同意你的看法。如果有人想要更深奥的 group by
——比如,省略列——那么他们可以使用子查询。
如果非要我猜的话,SQL 的作者不想在聚合函数中赋予如此强大的功能。您的建议意味着 select
中的函数正在确定结果集中行的定义。通常,select
只是确定列。也就是说,查询失败 语法 是一回事,因为包含的聚合没有 group by
。 select
中的函数更改正在输出的行是另一回事。
您可以将 window 函数与 select distinct
一起使用。虽然我不推荐语法,但你可以这样做:
select distinct x, count(*) over (partition by x)
from t;
好吧,这消除了 group by
,但您仍然需要在每个 window 函数中重复分组标准。
您必须想象您正在使用这里的 table 的两个版本。例如:
SELECT …
FROM table
GROUP BY …;
首先,请记住 SELECT
在 FROM
和 GROUP BY
子句之后计算。这意味着您可以 select 受到这些子句结果的限制。
假设 GROUP BY
生成一个新的 virtual table。此虚拟 table 只有以下内容:
- 分组的列
- 所有列的摘要(聚合)
- 没有别的
每个distinct组会有一排摘要。
如果您想要在 SELECT
子句中使用特定的列,它必须是组列或摘要,因为您只能 SELECT
可用的内容。
即使没有 GROUP BY
子句,也有一个隐含的 GROUP BY ()
会导致单行摘要。一些 DBMS(不是全部)甚至允许您添加它,尽管它不会改变任何东西。
请注意,GROUP BY
摘要中的行数受您要分组的列数的影响。一般来说,行数将类似于 (DISTINCT Group1)*(DISTINCT GROUP2) 等。这意味着您当然不希望按实际需要的数量进行分组。
异常
假设你有这样的陈述:
SELECT state, name, count(*)
FROM customers
GROUP BY state;
这当然会失败。一个状态有多个值的select和name
是什么意思?
但是,在传统模式下,MySQL 会让您做到这一点:它将 select 一个 名称与状态一起使用。但是,无法保证是哪一个,所以它的价值值得怀疑。
假设您要按月分组。您可能希望显示月份名称,但按月份编号排序。在这里,您需要按两者分组。使用伪日期函数:
SELECT monthname, count(*)
FROM data
GROUP BY monthname, monthnumber
ORDER BY monthnumber;
这只不过是一种解决方法。它利用了这样一个事实,即对于每个月份名称,只有一个月份编号,因此没有真正的进一步分组。它只是为了在 GROUP BY
虚拟 table.
中获取两个值
在写了几年SQL之后,我发现不得不把我感兴趣的栏目放在SELECT
中,然后在GROUP BY
中再次指定它们,这常常很烦人。我不禁想,为什么我们必须这样做?
用户必须具体说明分组依据的列的原因是什么?我们不能让 SQL 引擎假设 如果 SELECT
中有聚合函数,按其余非聚合列分组 ?
当您在 SELECT
中有一个大的 CASE WHEN
时,这将特别有用并且更加简洁。
因为它们可能并不总是完全匹配。
例如,如果我想找出每个类别的最大图书数量,我可以这样做:
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 子句中使用别名。
我碰巧有点同意你的看法。如果有人想要更深奥的 group by
——比如,省略列——那么他们可以使用子查询。
如果非要我猜的话,SQL 的作者不想在聚合函数中赋予如此强大的功能。您的建议意味着 select
中的函数正在确定结果集中行的定义。通常,select
只是确定列。也就是说,查询失败 语法 是一回事,因为包含的聚合没有 group by
。 select
中的函数更改正在输出的行是另一回事。
您可以将 window 函数与 select distinct
一起使用。虽然我不推荐语法,但你可以这样做:
select distinct x, count(*) over (partition by x)
from t;
好吧,这消除了 group by
,但您仍然需要在每个 window 函数中重复分组标准。
您必须想象您正在使用这里的 table 的两个版本。例如:
SELECT …
FROM table
GROUP BY …;
首先,请记住 SELECT
在 FROM
和 GROUP BY
子句之后计算。这意味着您可以 select 受到这些子句结果的限制。
假设 GROUP BY
生成一个新的 virtual table。此虚拟 table 只有以下内容:
- 分组的列
- 所有列的摘要(聚合)
- 没有别的
每个distinct组会有一排摘要。
如果您想要在 SELECT
子句中使用特定的列,它必须是组列或摘要,因为您只能 SELECT
可用的内容。
即使没有 GROUP BY
子句,也有一个隐含的 GROUP BY ()
会导致单行摘要。一些 DBMS(不是全部)甚至允许您添加它,尽管它不会改变任何东西。
请注意,GROUP BY
摘要中的行数受您要分组的列数的影响。一般来说,行数将类似于 (DISTINCT Group1)*(DISTINCT GROUP2) 等。这意味着您当然不希望按实际需要的数量进行分组。
异常
假设你有这样的陈述:
SELECT state, name, count(*)
FROM customers
GROUP BY state;
这当然会失败。一个状态有多个值的select和name
是什么意思?
但是,在传统模式下,MySQL 会让您做到这一点:它将 select 一个 名称与状态一起使用。但是,无法保证是哪一个,所以它的价值值得怀疑。
假设您要按月分组。您可能希望显示月份名称,但按月份编号排序。在这里,您需要按两者分组。使用伪日期函数:
SELECT monthname, count(*)
FROM data
GROUP BY monthname, monthnumber
ORDER BY monthnumber;
这只不过是一种解决方法。它利用了这样一个事实,即对于每个月份名称,只有一个月份编号,因此没有真正的进一步分组。它只是为了在 GROUP BY
虚拟 table.