我的讲师说:
SELECT列表中的所有列名必须出现在GROUP BY子句中,除非name仅用于聚合函数
我只是想要对此进行一些确认,因为我无法想出为什么它应该是真实的合理解释......
答案 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;