Преобразование MySQL в PostgreSQL

У меня есть этот запрос, который корректно работает в MySQL. Подробнее об этом здесь.

SELECT c.*, SUM(ABS(v.vote)) AS score
FROM categories c,items i, votes v
    WHERE c.id = i.category_id
    AND i.id = v.voteable_id
    AND v.created_at > '#{1.week.ago}'
GROUP BY c.id
ORDER BY score DESC LIMIT 8;

Я попытался запустить его в PostgreSQL, и с этим сообщением об ошибке оно не получило.

PGError: ОШИБКА: столбец "c.name" должен появляются в предложении GROUP BY или используется в агрегатной функции

Я не был уверен, что это значит, поэтому я попытался изменить "c.id" на "c.name" в предложении group by (оба работают в MySQL одинаково, при условии, что имя элемента уникально).

Однако это только что вызвало другую аналогичную ошибку

PGError: ОШИБКА: столбец "c.id" должен появляются в предложении GROUP BY или используется в агрегатной функции

Как решить эту проблему?

Ответы

Ответ 1

Вам нужно указать имена столбцов в SELECT, в которые вы группируетесь:

SELECT c.id, c.name, SUM(ABS(v.vote)) AS score
FROM categories c,items i, votes v
  WHERE c.id = i.category_id
  AND i.id = v.voteable_id
  AND v.created_at > '#{1.week.ago}'
GROUP BY c.id, c.name
ORDER BY score DESC LIMIT 8;

"Недопустимо включать имена столбцов в предложение SELECT, которые не указаны в предложении GROUP BY."

Ответ 2

У меня была эта проблема, но я перешел от MySQL к SQL Server. Я думал, что это разрешено, это было странно!

Да, в большинстве баз данных, когда у вас есть предложение GROUP BY, вы можете выбирать только агрегаты столбцов или столбцов, которые отображаются в предложении GROUP BY. Это связано с тем, что у него нет возможности узнать, действительно ли другие выбранные вами столбцы уникальны или нет.

Просто поместите нужные столбцы в GROUP BY, если они действительно уникальны. Это была "особенность" MySQL, которая была сомнительной.

Вы можете прочитать о поведении MySQL и о том, как он отличается здесь.

Пример:

SELECT c.*, SUM(ABS(v.vote)) AS score
FROM categories c,items i, votes v
    WHERE c.id = i.category_id
    AND i.id = v.voteable_id
    AND v.created_at > '#{1.week.ago}'
GROUP BY c.id, c.name, c.whatever_else
ORDER BY score DESC LIMIT 8;

Ответ 3

Если вы измените свое утверждение на это, оно должно работать:

SELECT c.id, SUM(ABS(v.vote)) AS score
FROM categories c,items i, votes v
  WHERE c.id = i.category_id
  AND i.id = v.voteable_id
  AND v.created_at > '#{1.week.ago}'
GROUP BY c.id
ORDER BY score DESC LIMIT 8;

Я не уверен, что MySQL дает в результате, но чтобы дать вам очень маленький пример того, почему это не работает в PostgreSQL, посмотрите на следующую таблицу categories:

id | name
---|-----
 1 | ABC
 1 | DEF

Вы группируете id, поэтому каждая строка в результате должна содержать только один id. Если вы также выберите name, не группируя его, что должно быть показано в результате для name?

Это может быть либо ABC, либо DEF, но механизм базы данных не может решить это для вас (хотя MySQL, по-видимому, делает это).