PostgreSQL GROUP BY отличается от MySQL?

Я переносил некоторые из моих MySQL-запросов в PostgreSQL для использования Heroku. Большинство моих запросов работают нормально, но я сохраняю подобную повторяющуюся ошибку, когда я использую группу:

ОШИБКА: столбец "XYZ" должен появиться в предложении GROUP BY или использоваться в совокупная функция

Может ли кто-нибудь сказать мне, что я делаю неправильно?


MySQL, который работает на 100%:

SELECT `availables`.*
FROM `availables`
INNER JOIN `rooms` ON `rooms`.id = `availables`.room_id
WHERE (rooms.hotel_id = 5056 AND availables.bookdate BETWEEN '2009-11-22' AND '2009-11-24')
GROUP BY availables.bookdate
ORDER BY availables.updated_at


Ошибка PostgreSQL:

ActiveRecord:: StatementInvalid: PGError: ОШИБКА: столбец "availables.id" должен появиться в предложении GROUP BY или использоваться в агрегированная функция:
SELECT "Доступно". * FROM "Доступные" INNER JOIN "номера" ON "номера".id = "гостинные".room_id ГДЕ (rooms.hotel_id = 5056 AND availableables.bookdate BETWEEN E'2009-10-21 ' И E'2009-10-23 ') ГРУППА BY Доступные. Bookdate ORDER BY availables.updated_at


Ruby-код, генерирующий SQL:

expiration = Available.find(:all,
    :joins => [ :room ],
    :conditions => [ "rooms.hotel_id = ? AND availables.bookdate BETWEEN ? AND ?", hostel_id, date.to_s, (date+days-1).to_s ],
    :group => 'availables.bookdate',
    :order => 'availables.updated_at')  


Ожидаемый результат (из рабочего запроса MySQL):

+-----+-------+-------+------------+---------+---------------+---------------+
| id  | price | spots | bookdate   | room_id | created_at    | updated_at    |
+-----+-------+-------+------------+---------+---------------+---------------+
| 414 | 38.0  | 1     | 2009-11-22 | 1762    | 2009-11-20... | 2009-11-20... |
| 415 | 38.0  | 1     | 2009-11-23 | 1762    | 2009-11-20... | 2009-11-20... |
| 416 | 38.0  | 2     | 2009-11-24 | 1762    | 2009-11-20... | 2009-11-20... |
+-----+-------+-------+------------+---------+---------------+---------------+
3 rows in set

Ответ 1

MySQL, полностью совместимый со стандартами GROUP BY, может быть эмулирован Postgres DISTINCT ON. Рассмотрим это:

mysql:

SELECT a,b,c,d,e FROM table GROUP BY a

Это поставляет 1 строку на одно значение (которое вы действительно не знаете). Ну, на самом деле вы можете догадаться, потому что MySQL не знает об агрегатах хэша, поэтому он, вероятно, будет использовать сортировку... но он будет сортировать только по a, поэтому порядок строк может быть случайным. Если вместо сортировки он использует индекс многоколонки. Ну, в любом случае, это не указано в запросе.

postgres:

SELECT DISTINCT ON (a) a,b,c,d,e FROM table ORDER BY a,b,c

Это обеспечивает 1 строку за значение a, эта строка будет первой в сортировке в соответствии с ORDER BY, указанной в запросе. Простой.

Обратите внимание, что здесь это не совокупность, которую я вычисляю. Поэтому GROUP BY на самом деле не имеет смысла. DISTINCT ON имеет гораздо больше смысла.

Rails замужем за MySQL, поэтому я не удивлен, что он генерирует SQL, который не работает в postgres.

Ответ 2

PostgreSQL более совместим с SQL, чем MySQL. Все поля - кроме вычисленного поля с функцией агрегации - в выводе должны присутствовать в предложении GROUP BY.

Ответ 3

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

Ответ 4

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

Отвратительно - но это то, как группа должна работать в противоположность тому, как MySQL работает с ней, угадывая, что вы имеете в виду, если вы не вставляете поля в свою группу.

Ответ 5

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

Ответ 6

Не самое приятное решение, но изменение параметра группы для вывода каждого столбца в модели работает в PostgreSQL:

expiration = Available.find(:all,
:joins => [ :room ],
:conditions => [ "rooms.hotel_id = ? AND availables.bookdate BETWEEN ? AND ?", hostel_id, date.to_s, (date+days-1).to_s ],
:group => Available.column_names.collect{|col| "availables.#{col}"},
:order => 'availables.updated_at')

Ответ 7

Согласно MySQL "Debuking GROUP BY Myths" http://dev.mysql.com/tech-resources/articles/debunking-group-by-myths.html. SQL (версия стандарта стандарта 2003) не требует, чтобы столбцы, указанные в списке SELECT запроса, также отображались в предложении GROUP BY.

Ответ 8

Для других, которые ищут способ заказа по любому полю, включая объединенное поле, в postgresql используют подзапрос:

SELECT * FROM(
SELECT DISTINCT ON(availables.bookdate) `availables`.* 
FROM `availables` INNER JOIN `rooms` ON `rooms`.id = `availables`.room_id 
WHERE (rooms.hotel_id = 5056 
AND availables.bookdate BETWEEN '2009-11-22' AND '2009-11-24')
) AS distinct_selected
ORDER BY availables.updated_at

or arel:

subquery = SomeRecord.select("distinct on(xx.id) xx.*, jointable.order_field")
      .where("").joins(")
result = SomeRecord.select("*").from("(#{subquery.to_sql}) AS distinct_selected").order(" xx.order_field ASC, jointable.order_field ASC")