Все ли столбцы в списке SELECT должны появляться в предложении GROUP BY - PullRequest
42 голосов
/ 13 мая 2011

Мой лектор заявил:

Все имена столбцов в списке SELECT должны появляться в предложении GROUP BY, если только имя не используется только в статистической функции

Я просто хочу получить некоторое подтверждение этого, так как не могу придумать логического объяснения, почему это должно быть правдой ...

Ответы [ 7 ]

31 голосов
/ 13 мая 2011

Представьте себе следующее:

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

Если вы выберете A, B и Group By Only A - какими будут ваши результаты? У вас будет только две строки (или кортежи), потому что у вас есть два значения для A - но как оно отображает B?

Если вы сгруппируете по A, B, вы получите четыре строки, без проблем. Если вы группируете по A и выполняете функцию для B - как SUM (B), то вы снова получаете две строки:

    Cat    15
    Dog    45

Но если вы выберете A, B и сгруппируете только по A - он не знает, что делать. По правде говоря, я полагаю, что есть некоторые базы данных, которые выберут случайное значение для B в этом случае, и я полагаю, что есть некоторые, которые выдадут вам сообщение об ошибке.

16 голосов
/ 13 мая 2011

Это исторически верно. Пропуск неагрегированных столбцов ведет к неопределенному поведению. 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.

7 голосов
/ 13 мая 2011

На самом деле, в MySQL вам не нужно группировать по всем столбцам.Вы можете просто группировать по любым столбцам, которые вы хотите.Проблема в том, что он просто извлекает случайное значение (из набора доступных строк в группе) для полей, которых нет в группе.Если вы знаете, что группируете по чему-то, что является уникальным ключом, нет смысла группировать по остальным полям, так как они все равно будут иметь одинаковое значение.Это может на самом деле ускорить его, чтобы не приходилось группировать по каждому полю, когда это совершенно не нужно.

2 голосов
/ 10 января 2013

Итак, простой ответ: это зависит. Mysql позволяет это, Vertica нет.

На самом деле есть допустимый вариант использования для пропуска, и это когда вы уже выбираете сказать с помощью MIN ().

Вот фактический пример для отслеживания событий. Imaging у вас есть кредит и покупки событий.

Для простоты мы говорим, что 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:

ОШИБКА 2640: Столбец "e2.event" должен появляться в предложении GROUP BY или использоваться в статистической функции

Если я опускаю столбец события, он работает в обоих случаях, но на самом деле я хочу знать, какое конкретное значение события имело для строки, выбранной min.

Так что мой ответ заканчивается запросом комментария :) Есть идеи?

1 голос
/ 13 мая 2011

Существуют исключения, как отметил Сэм Саффрон, но в целом то, что сказал ваш лектор, верно.

Если я выберу 3 столбца и сгруппирую по 2, что СУРБД будет делать с 3-м столбцом?

Разработчики СУБД могут принять решение о том, как обрабатывать лишний столб (как, кажется, разработчики MySQL), но это решение я бы принял или тот, который я хочу при написании выбора?Будет ли решение всегда действительным?Я, конечно, предпочитаю Oracle-подобный подход, заставляющий меня явно указывать, что должно произойти.

Если я выберу 3 столбца и сгруппирую по 2, должна ли RDBS группировать по всем 3, выбрать случайное значение из 3-го, наибольшего или наименьшего, самого распространенного?

1 голос
/ 13 мая 2011

Если вы группируете что-то, вы не можете видеть отдельные значения не сгруппированных столбцов, поскольку в каждой группе может быть более одного значения.Все, что вы можете сделать, это сообщить об агрегатных функциях (сумма, число, мин и т. Д.) - они могут объединить несколько значений в одну ячейку в результате.

0 голосов
/ 25 декабря 2013

Это ответ на пример / вопрос Майкла Уилла.

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...