Почему нет "SELECT foo. * ... GROUP BY foo.id" в Postgres? - PullRequest
11 голосов
/ 16 июля 2009

У меня такой запрос:

select foo.*, count(bar.id)
from foo inner join bar on foo.id = bar.foo_id
group by foo.id

Это прекрасно работает с SQLite и MySQL. Однако Postgres жалуется на то, что я не включил все столбцы foo в предложение group by. Почему это? Разве мало того, что foo.id уникален?

Ответы [ 4 ]

31 голосов
/ 01 июня 2012

На всякий случай, если другие люди наткнуться на этот вопрос:

Начиная с PostgreSQL 9.1, достаточно перечислить столбцы первичного ключа в выражении group by (так что пример из вопроса будет работать сейчас).

5 голосов
/ 16 июля 2009

Некоторые базы данных более спокойны по этому поводу, хорошо это и плохо. Запрос не является специфичным, поэтому результат также не является специфичным. Если база данных разрешит запрос, она вернет по одной записи из каждой группы, и не важно, какая из них. Другие базы данных являются более конкретными и требуют, чтобы вы указали, какое значение вы хотите получить из группы. Они не позволят вам написать запрос с неопределенным результатом.

Единственные значения, которые вы можете выбрать без агрегата, это значения в предложении group by:

select foo.id, count(bar.id)
from foo inner join bar on foo.id = bar.foo_id
group by foo.id

Вы можете использовать агрегаты для получения других значений:

select foo.id, min(foo.price), count(bar.id)
from foo inner join bar on foo.id = bar.foo_id
group by foo.id

Если вам нужны все значения из таблицы foo, вы можете поместить их все в предложение group by (если это дает правильный результат):

select foo.id, foo.price, foo.name, foo.address, count(bar.id)
from foo inner join bar on foo.id = bar.foo_id
group by foo.id, foo.price, foo.name, foo.address

Или вы можете присоединиться к таблице с помощью подзапроса:

select foo.id, foo.price, foo.name, foo.address, sub.bar_count
from foo
inner join (
   select foo.id, bar_count = count(bar.id)
   from foo inner join bar on foo.id = bar.foo_id
   group by foo.id
) sub on sub.id = foo.id
3 голосов
/ 16 июля 2009

Что бы вы хотели получить с помощью postgresql? Вы используете агрегатную функцию и пытаетесь вывести «что-то».

Ах. Я вижу, что вы можете сделать. Используйте подвыбор.

select foo.*, (select count(*) from bar where bar.foo_id=foo.id) from foo;

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

Да, в теории было бы достаточно группировки по foo.id (т.е. ваш запрос плюс "group by foo.id"). Но, по-видимому (я проверял это) postgresql не будет этого делать. Другой вариант - «группировать по foo.id, foo.foo, foo.bar, foo.baz» и всему остальному, что находится в «foo. *».

Другой способ, которым занимается Гуффа, таков:

SELECT foo.*, COALESCE(sub.cnt, 0)
FROM foo
LEFT OUTER JOIN (
  SELECT foo_id, count(*) AS cnt
  FROM bar
  GROUP BY foo_id) sub
ON sub.foo_id = foo.id;

Это будет два запроса (один подзапрос, который выполняется только один раз), что может иметь значение, но, вероятно, не будет. Если вы можете просто обойтись без «foo. *», Вы можете использовать вторую версию, которая явно группирует по всем столбцам.

1 голос
/ 16 июля 2009

Предложение GROUP BY требует, чтобы каждый столбец, возвращаемый запросом, был либо столбцом, содержащимся в операторе GROUP BY, либо агрегатной функцией (например, COUNT в вашем примере). Не видя, что является вашим GROUP BY предложением или что такое столбцы foo, трудно сказать, что именно происходит, но я думаю, проблема в том, что foo.* пытается вернуть один или несколько столбцов, которые не в вашем GROUP BY предложении.

Это действительно общее свойство SQL и не должно быть специфичным для PostgreSQL. Понятия не имею, почему он работает для вас с SQLite или MySQL - возможно, все столбцы в foo.* на самом деле находятся в вашем предложении GROUP BY, но PostgreSQL не может понять это - поэтому попробуйте перечислить все столбцы foo явно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...