Является ли SQL GROUP ошибкой дизайна?
Почему SQL требует, чтобы я указывал, какие атрибуты группировать? Почему он не может использовать все неагрегаты?
Если атрибут не агрегирован и не находится в предложении GROUP BY, то недетерминированный выбор был бы единственным вариантом, предполагающим, что кортежи неупорядочены (тип mysql делает это), и это огромная добыча. Насколько мне известно, Postgresql требует, чтобы все атрибуты, не появляющиеся в GROUP BY, должны быть агрегированы, что усиливает его избыточность.
- Я что-то упустил или это ошибка дизайна языка, которая способствует свободным реализациям и затрудняет запись запросов?
- Если я что-то упустил, что такое примерный запрос, где невозможно определить логические атрибуты группы?
Ответы
Ответ 1
Вам не нужно группировать то же самое, что вы выбираете, например.
SQL:select priority,count(*) from rule_class
group by priority
PRIORITY COUNT(*)
70 1
50 4
30 1
90 2
10 4
SQL:select decode(priority,50,'Norm','Odd'),count(*) from rule_class
group by priority
DECO COUNT(*)
Odd 1
Norm 4
Odd 1
Odd 2
Odd 4
SQL:select decode(priority,50,'Norm','Odd'),count(*) from rule_class
group by decode(priority,50,'Norm','Odd')
DECO COUNT(*)
Norm 4
Odd 8
Ответ 2
Есть еще одна причина для , почему SQL требует, чтобы я указывал, какие атрибуты для группы.
У нас есть две простые таблицы: friend
и car
, где мы храним информацию о наших друзьях и их автомобилях.
И скажем, мы хотим показать все данные наших друзей (из таблицы friend
) и для всех наших друзей, сколько автомобилей, которыми они владеют сейчас, продали, разбились и общее число. О, и мы хотим сначала старших, младших.
Мы сделали бы что-то вроде:
SELECT f.id
, f.firstname
, f.lastname
, f.birthdate
, COUNT(NOT c.sold AND NOT c.crashed) AS owned
, COUNT(c.sold) AS sold
, COUNT(c.crashed) AS crashed
, COUNT(c.friendid) AS totalcars
FROM friend f
LEFT JOIN car c <--to catch (shame!) those friends who have never had a car
ON f.id = c.friendid
GROUP BY f.id
, f.firstname
, f.lastname
, f.birthdate
ORDER BY f.birthdate DESC
Но действительно ли нам нужны все эти поля в GROUP BY
? Разве не каждый друг однозначно определяется его id
? Другими словами, не функциональная зависимость firstname, lastname and birthdate
от f.id
? Почему бы просто не сделать (как мы можем в MySQL):
SELECT f.id
, f.firstname
, f.lastname
, f.birthdate
, COUNT(NOT c.sold AND NOT c.crashed) AS owned
, COUNT(c.sold) AS sold
, COUNT(c.crashed) AS crashed
, COUNT(c.friendid) AS totalcars
FROM friend f
LEFT JOIN car c <--to catch (shame!) those friends who have never had a car
ON f.id = c.friendid
GROUP BY f.id
ORDER BY f.birthdate
А что, если бы у нас было 20 полей в частях SELECT
(плюс ORDER BY
)? Не второй запрос короче, яснее и, вероятно, быстрее (в СУБД, которые его принимают)?
Я говорю "да". Итак, скажем, спецификации SQL 1999 и 2003, если эта статья верна: Группа разрывов по мифам
Ответ 3
Я бы сказал, если у вас есть большое количество элементов в предложении group by, то, возможно, основная информация должна быть выведена в табличный подзапрос, к которому вы присоединяетесь.
Вероятно, есть хит производительности, но он делает более быстрый код.
select id, count(a), b, c, d
from table
group by
id, b, c, d
становится
select id, myCount, b, c, d
from table t
inner join (
select id, count(*) as myCount
from table
group by id
) as myCountTable on myCountTable.id = t.id
Тем не менее, мне интересно услышать встречные аргументы для этого, а не для предложения большой группы.
Ответ 4
Я согласен с тем, что группа по списку не должна быть точно такой же, как и неагрегированные столбцы выбора. В Sas операции с агрегацией данных более сжатые.
Также: трудно найти пример, где было бы полезно иметь более длинный список столбцов в списке групп, чем список выбора. Лучшее, что я могу придумать, это...
create table people
( Nam char(10)
,Adr char(10)
)
insert into people values ('Peter', 'Tibet')
insert into people values ('Peter', 'OZ')
insert into people values ('Peter', 'OZ')
insert into people values ('Joe', 'NY')
insert into people values ('Joe', 'Texas')
insert into people values ('Joe', 'France')
-- Give me people where there is a duplicate address record
select * from people where nam in
(
select nam
from People
group by nam, adr -- group list different from select list
having count(*) > 1
)
Ответ 5
Если вы просто примените более простой способ написания сценариев.
Вот один совет:
В MS SQL MGMS напишите запрос в тексте как-то вроде select * from my_table
после этого выберите текст правой кнопкой мыши и "Запросить дизайн в редакторе".
Студия Sql откроет новый редактор, после чего снова щелкнет все поля и выберите "Добавить Gruop BY",
Студия Sql MGM добавит вам код.
Я финансирую этот метод, чрезвычайно полезный для операторов insert. Когда мне нужно написать script для вставки большого количества полей в таблицу, я просто делаю select * из table_where_want_to_insert и после этого типа изменения в инструкции select для вставки
Ответ 6
Я согласен
Я вполне согласен с этим вопросом. Я спросил об этом здесь.
Я честно считаю это языковым недостатком.
Я понимаю, что есть аргументы против этого, но я еще не использовал предложение GROUP BY, содержащее ничего, кроме всех неагрегированных полей из предложения SELECT в реальном мире.
Ответ 7
Этот поток содержит некоторые полезные объяснения.
http://social.msdn.microsoft.com/Forums/en/transactsql/thread/52482614-bfc8-47db-b1b6-deec7363bd1a
Ответ 8
Я бы сказал, что скорее вариант выбора языка, чтобы решения были явными, а не подразумеваемыми. Например, что, если я хочу сгруппировать данные в другом порядке, чем тот, в котором я выводю столбцы? Или, если я хочу группировать по столбцам, которые не включены в выбранные столбцы? Или если я хочу выводить только столбцы сгруппированные и не использовать агрегированные функции? Только явным образом заявляю о своих предпочтениях в группе по статье, мои намерения ясны.
Вы также должны помнить, что SQL - очень старый язык (1970). Посмотрите, как Linq перевернул все вокруг, чтобы заставить Intellisense работать - теперь это выглядит очевидно, но SQL предшествует IDE и поэтому не может принимать во внимание такие проблемы.
Ответ 9
Атрибуты "superflous" влияют на упорядочение результата.
Рассмотрим:
create table gb (
a number,
b varchar(3),
c varchar(3)
);
insert into gb values ( 3, 'foo', 'foo');
insert into gb values ( 1, 'foo', 'foo');
insert into gb values ( 0, 'foo', 'foo');
insert into gb values ( 20, 'foo', 'bar');
insert into gb values ( 11, 'foo', 'bar');
insert into gb values ( 13, 'foo', 'bar');
insert into gb values ( 170, 'bar', 'foo');
insert into gb values ( 144, 'bar', 'foo');
insert into gb values ( 130, 'bar', 'foo');
insert into gb values (2002, 'bar', 'bar');
insert into gb values (1111, 'bar', 'bar');
insert into gb values (1331, 'bar', 'bar');
Это утверждение
select sum(a), b, c
from gb
group by b, c;
приводит к
44 foo bar
444 bar foo
4 foo foo
4444 bar bar
в то время как этот
select sum(a), b, c
from gb
group by c, b;
приводит к
444 bar foo
44 foo bar
4 foo foo
4444 bar bar