Преобразование 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, по-видимому, делает это).