Ответ 1
На всякий случай другие люди спотыкаются над этим вопросом:
Начиная с PostgreSQL 9.1 достаточно перечислить столбцы первичного ключа в предложении group by (так что пример из вопроса будет работать сейчас).
У меня есть такой запрос:
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 уникален?
На всякий случай другие люди спотыкаются над этим вопросом:
Начиная с PostgreSQL 9.1 достаточно перечислить столбцы первичного ключа в предложении group by (так что пример из вопроса будет работать сейчас).
Что именно у вас будет выход postgresql? Вы используете агрегатную функцию и пытаетесь вывести "что-то".
Ах. Я вижу, что вы можете сделать. Используйте подзапрос.
select foo.*, (select count(*) from bar where bar.foo_id=foo.id) from foo;
Проверьте с объяснением, что план выглядит неплохо. Подсекция не всегда плохая. Я просто проверил с помощью базы данных, которую я использую, и мой план выполнения был хорош для этого запроса.
Да, в теории группирования по foo.id будет достаточно (т.е. ваш запрос плюс "группа by foo.id" ). Но, по-видимому (я его протестировал) postgresql этого не сделает. Другой вариант - "group by 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. *", Вы можете использовать вторую версию, которая явно группирует все столбцы.
Некоторые базы данных более расслаблены по этому поводу, для хорошего и плохого. Запрос неспецифичен, поэтому результат в равной степени неспецифичен. Если база данных разрешает запрос, она возвращает одну запись из каждой группы, и ее не волнует какой из них. Другие базы данных более конкретны и требуют, чтобы указать, какое значение вы хотите от группы. Они не позволят вам написать запрос с неспецифическим результатом.
Единственными значениями, которые вы можете выбрать без агрегата, являются те, что указаны в предложении 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
A GROUP BY
требует, чтобы каждый столбец, возвращаемый запросом, был либо столбцом, содержащимся в инструкции GROUP BY
, либо функцией агрегата (например, COUNT
в вашем примере). Не видя, что такое предложение GROUP BY
или что такое столбцы foo
, трудно сказать, что именно происходит, но я думаю, проблема в том, что foo.*
пытается вернуть один или несколько столбцов, которые не в вашем предложении GROUP BY
.
Это действительно общее свойство SQL и не должно быть специфичным для PostgreSQL. Не знаю, почему это сработало для вас с SQLite или MySQL - возможно, все столбцы в foo.*
на самом деле находятся в вашем предложении GROUP BY
, но PostgreSQL не может понять это - попробуйте указать все столбцы foo
явно.