Является ли 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 в реальном мире.

Ответ 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