Почему я не могу исключить зависимые столбцы из GROUP BY при агрегировании по ключу? - PullRequest
3 голосов
/ 30 мая 2020

Если у меня есть следующие таблицы (например, с использованием PostgreSQL, но может быть любая другая реляционная база данных), где car имеет два ключа (id и vin):

create table car (
  id int primary key not null,
  color varchar(10),
  brand varchar(10),
  vin char(17) unique not null
);

create table appraisal (
  id int primary key not null,
  recorded date not null,
  car_id int references car (id),
  car_vin char(17) references car (vin),
  price int
);

Я могу успешно включить c.color и c.brand в список выбора без их агрегирования, поскольку они зависят от c.id:

select 
  c.id, c.color, c.brand,
  min(price) as min_appraisal,
  max(price) as max_appraisal
from car c
left join appraisal a on a.car_id = c.id
group by c.id; -- c.color, c.brand are not needed here

Однако следующий запрос не выполняется, поскольку он не позвольте мне включить c.color и c.brand в список выбора, даже если это зависит от c.vin (это ключ) таблицы.

select 
  c.vin, c.color, c.brand,
  min(price) as min_appraisal,
  max(price) as max_appraisal
from car c
left join appraisal a on a.car_vin = c.vin
group by c.vin; -- Why are c.color, c.brand needed here?

Ошибка: ERROR : столбец «c .color» должен появиться в предложении GROUP BY или использоваться в агрегатной функции. Позиция: 18

Пример в DB Fiddle .

1 Ответ

4 голосов
/ 30 мая 2020

Потому что только PK покрывает все столбцы базовой таблицы в предложении GROUP BY. Следовательно, ваш первый запрос работает. Ограничение UNIQUE не работает.

Комбинация неотложного ограничения UNIQUE и NOT NULL также может быть квалифицирована. Но это не реализовано, как и некоторые другие функциональные зависимости, известные стандарту SQL. Питер Эйзентраут, главный автор статьи, имел в виду большее, но в то время было определено, что спрос низкий, а связанные с этим расходы могут быть высокими. См. Обсуждение этой функции на pg sql -hackers .

Руководство:

Когда присутствует GROUP BY , или присутствуют какие-либо агрегатные функции, выражения списка SELECT не могут ссылаться на несгруппированные столбцы, кроме как внутри агрегатных функций, или когда несгруппированный столбец функционально зависит от сгруппированных столбцов, поскольку в противном случае было бы более одного возможного значение, возвращаемое для несгруппированного столбца. Функциональная зависимость существует, если сгруппированные столбцы (или их подмножество) являются первичным ключом таблицы, содержащей несгруппированный столбец.

И более явно:

PostgreSQL распознает функциональную зависимость (позволяя опускать столбцы из GROUP BY) только тогда, когда первичный ключ таблицы включен в список GROUP BY. Стандарт SQL определяет дополнительные условия, которые следует распознавать.

Поскольку c.vin равно UNIQUE NOT NULL, вы можете исправить свой второй запрос, используя вместо этого столбец PK:

...
group by c.id;

Кроме того, хотя ссылочная целостность обеспечивается и запрашивается вся таблица, оба данных запроса могут быть существенно дешевле: агрегировать строки в appraisal перед объединением. Это устраняет необходимость GROUP BY во внешнем SELECT априори. Например:

SELECT c.vin, c.color, c.brand
     , a.min_appraisal
     , a.max_appraisal
FROM   car c
LEFT   JOIN (
   SELECT car_vin
        , min(price) AS min_appraisal
        , max(price) AS max_appraisal
   FROM   appraisal
   GROUP  BY car_vin
   ) a ON a.car_vin = c.vin;

См.:

Связано:

...