SELECT列表中的所有列都必须出现在GROUP BY子句中

时间:2011-05-13 00:45:38

标签: sql

我的讲师说:

SELECT列表中的所有列名必须出现在GROUP BY子句中,除非name仅用于聚合函数

我只是想要对此进行一些确认,因为我无法想出为什么它应该是真实的合理解释......

7 个答案:

答案 0 :(得分:31)

想象一下:

    A    B    C
   Cat   10   False
   Dog   25   True
   Dog   20   False
   Cat   5    False

如果您选择A,B和仅按A组 - 您的输出会是什么?你只有两行(或元组),因为你有两个A值 - 但它如何显示B?

如果按A,B分组,你会得到四行,没有问题。 如果按A分组并在B上执行函数 - 如SUM(B),则会再次获得两行:

    Cat    15
    Dog    45

但是如果你选择A,B并且只选择A组 - 它不知道该怎么做。说实话,我相信有一些数据库会在这种情况下为B选择一个随机值,我相信有一些会给你一个错误信息。

答案 1 :(得分:16)

这在历史上是正确的。省略未聚合的列会导致不确定的行为。 SQL旨在完全确定行为。

但SQL标准最近已更改为允许您省略GROUP BY子句列,这些列在功能上依赖于GROUP BY中 的列。 PostgreSQL遵循最新的SQL标准。 (这不是唯一的。)行为仍然是完全确定的。

create table a (
  a_id integer primary key,
  xfr_date date not null
);

create table b (
  a_id integer not null references a (a_id),
  recd_date date not null,
  units_recd integer not null 
    check (units_recd >= 0),
  primary key (a_id, recd_date)
);

select a.a_id, a.xfr_date, sum(b.units_recd)
from a
inner join b on a.a_id = b.a_id
group by a.a_id; -- The column a.xfr_date is functionally dependent 
                 -- on a.a_id; it doesn't have to appear in the 
                 -- GROUP BY clause.

与SQL标准的显着偏差是MySQL。它允许您省略GROUP BY中的所有内容。但是,当您省略SELECT列表中的列时,该设计选择会使其行为不确定。

答案 2 :(得分:7)

实际上,在MySQL中,您不必按所有列进行分组。您可以按所需的列分组。问题是,它只会为不在组中的字段提取一个随机值(来自组中可用行的集合)。如果你知道你正在通过一个唯一键的东西进行分组,那么对其他字段进行分组就没有意义了,因为无论如何它们都已经具有相同的值。它实际上可以加速,以便在完全没有必要时不必按每个字段进行分组。

答案 3 :(得分:1)

如果要对某些内容进行分组,则无法看到未分组列的各个值,因为每个组中可能有多个值。您所能做的只是报告聚合函数(总和,计数,分钟和等) - 它们能够将多个值组合到结果中的单个单元格中。

答案 4 :(得分:1)

Sam Saffron指出有例外情况,但一般来说,你的讲师说的是真的。

如果我选择3列并按2分组RDBMS应该对第3列做什么?

RDBMS的开发人员可能会决定如何处理额外的列(正如MySQL的开发人员所看到的那样),但这是我在编写选择时做出的决定还是我想要的决定?决定总是有效吗?我当然更喜欢类似Oracle的方法,迫使我明确说明应该发生什么。

如果我选择3列并且按2分组应该RDBS组全部3,从第3个中选择一个随机值,最大或最小,最常见?

答案 5 :(得分:1)

所以简单的答案是:这取决于。 Mysql允许它,vertica没有。

实际上有一个有效的用例省略,那就是你已经选择了使用MIN()的说法。

以下是事件跟踪的实际示例。想象您有信用和购买活动。

为简单起见,我们说a = credit,b,c,d是某种购买事件,并且使用正在运行的号码跟踪时间。现在,您想要在每个信用额后找到第一次购买的日期。我们碰巧只有一个客户0:

create table events (user_id int ,created_at int, event varchar(255));
insert into events values (0,0, 'a');
insert into events values (0,1, 'b');
insert into events values (0,2, 'c');
insert into events values (0,3, 'a');
insert into events values (0,4, 'c');
insert into events values (0,5, 'b');
insert into events values (0,6, 'a');
insert into events values (0,7, 'a');
insert into events values (0,8, 'd');

mysql> SELECT user_id, MAX(purchased) AS purchased, spent, event FROM (SELECT e1.User_ID AS user_id, e1.created_at AS purchased, MIN(e2.created_at) AS spent, e2.event AS event FROM events e1, events e2 WHERE e1.user_id = e2.user_id AND e1.created_at <= e2.created_at AND e1.Event = 'a' AND e2.Event != 'a' GROUP BY e1.user_id, e1.created_at) e3 GROUP BY user_id, spent;
+---------+-----------+-------+-------+
| user_id | purchased | spent | event |
+---------+-----------+-------+-------+
|       0 |         0 |     1 | b     |
|       0 |         3 |     4 | c     |
|       0 |         7 |     8 | d     |
+---------+-----------+-------+-------+
3 rows in set (0.00 sec)

在mysql中看起来不错,在vertica中不起作用:

ERROR 2640:列“e2.event”必须出现在GROUP BY子句中或用于聚合函数

如果我省略了事件列,它在两者中都有效,但我确实想知道具有最小选择行的特定值事件。

所以我的回答以评论请求结束:)任何想法?

答案 6 :(得分:0)

这是Michael Will的例子/问题的答案。

SELECT 
    e3.user_id,
    MAX(e3.purchased) AS purchased, 
    e3.spent, 
    e.event
FROM 
    events e
INNER JOIN
(SELECT 
    e1.user_id AS user_id, 
    MIN(e1.created_at) as spent,
    e2.created_at as purchased
 FROM
    events e1
 INNER JOIN
    (SELECT e.user_id, e.created_at from events e WHERE e.event = 'a') e2   
 ON e1.user_id = e2.user_id 
 AND e1.created_at >= e2.created_at 
 AND e1.event != 'a'
 GROUP BY e1.User_ID, e2.created_at
) e3 
ON e.user_id = e3.user_id AND e.created_at = e3.spent
GROUP BY e3.user_id, e3.spent, e.event;