PostgreSQL GROUP BY与MySQL不同吗?


70

我一直在将一些MySQL查询迁移到PostgreSQL以使用Heroku。我的大多数查询工作正常,但是当使用group by时,我仍然遇到类似的重复错误:

错误:“ XYZ”列必须出现在GROUP BY子句中或在聚合函数中使用

有人可以告诉我我在做什么错吗?


可以100%运行的MySQL:

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:PG错误:错误:列“ availables.id”必须出现在GROUP BY子句中或在聚合函数中使用:
SELECT“ availables”。* FROM“ availables” INNER JOIN“ rooms” ON“ rooms”。 id =“ availables” .room_id在哪里(rooms.hotel_id = 5056 AND availables.bookdate在E'2009-10-21'和E'2009-10-23'之间)GROUP BY availables.bookdate订单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 | 价格| 点| 预定日期 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 ... |
+ ----- + ------- + ------- + ------------ + --------- + ---- ----------- + --------------- +
一套三排

如此...在预定日期使用独特功能会更好吗?如果这样做,我是否仍需要group by子句?
霍顿

2
DISTINCT比慢GROUP BY。因此,您应该小心并GROUP BY尽可能选择一种解决方案。
弗朗兹,

Answers:


110

GROUP BYPostgres可以模拟完全不符合MySQL标准的MySQL DISTINCT ON。考虑一下:

MySQL:

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

每个值传送1行a(您实际上并不知道该行)。好吧,实际上您可以猜测,因为MySQL不了解哈希聚合,因此它可能会使用sort ...,但它只会在sort上进行排序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中不起作用,我并不感到惊讶。


6
不过,除此之外,如果表的主键是group by子句的一部分,则Postgres 9.1允许不列出所有列。
Denis de Bernardy 2011年

5
根据本文“揭穿GROUP BY神话”的说法,它与“不符合标准的GROUP BY”无关。
拉法2012年

5
根据本文,MySQL的GROUP BY仍然不符合该标准的两个版本,因为它不验证selectlist中的额外列是否依赖于group by列。它将输出不正确的数据而不会发出警告(但也可以用于有用的目的)。PG 9.1假定增加表的PK意味着所有其他列都是相关的,这是正确的。这不包括标准的100%(其他正确的查询可能被标记为错误),但涵盖了大多数用例,而没有返回错误的结果...
bobflux 2012年

7
“ Rails与MySQL结为夫妻,因此它生成的SQL在postgres中不起作用,对此我并不感到惊讶。” 我认为这不再是对的,因为Postgres因其noSQL功能而在Rails社区中变得非常流行。
yagooar

4
Rails不再嫁给MySQL。
superluminary 2014年

17

PostgreSQL比MySQL更符合SQL。输出中的所有字段(具有聚合功能的计算字段除外)必须出现在GROUP BY子句中。


11

MySQL的GROUP BY可以在没有聚合函数的情况下使用(这与SQL标准相反),并返回组中的第一行(基于什么条件我不知道),而PostgreSQL必须具有聚合函数(MAX, SUM等)在发出GROUP BY子句的列上。


5

正确,解决此问题的方法是使用:select和选择要用其修饰结果对象并按其分组的每个字段。

讨厌的-但它是如何通过组应该努力,而不是到MySQL如何与它的工作原理是通过猜测你的意思,如果你不通过粘领域的小组。


1
我想无论您喜欢哪种形容词,MySQL都使我宠坏或毁了我,所以没有更好的办法吗?就是 抛出一个聚合函数,例如上面告诉我的MAX(bookdate)或DISTINCT慢得多?
霍顿

我会坚持分组依据-但要谨慎行事,尤其是因为您必须手动选择要用来装饰对象的字段。考虑到MSSQL(如果您不幸不得不使用它)和Oracle也将以类似的方式抱怨,因此还要编写与group by一起使用的手动select是一种与数据库无关的方法。
奥马尔·库雷希

DISTINCT并不一定意味着速度变慢。

3

如果我没有记错,在PostgreSQL里你必须增加每次从那里GROUP BY子句应用于表获取列GROUP BY子句。


2

不是最漂亮的解决方案,但是更改group参数以输出模型中的每个列都可以在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')

1

根据MySQL的“ Debuking GROUP BY Myths”,http: //dev.mysql.com/tech-resources/articles/debunking-group-by-myths.html 。SQL(标准的2003版本)不需要查询的SELECT列表中引用的列也可以出现在GROUP BY子句中。


1
但是,正如其他人指出,它不要求他们是在其列“函数依赖”GROUP BY。MySQL引用任何非分组列的能力完全是非标准的,并且允许用户编写不合逻辑且不可靠的查询。
IMSoP 2013年

当时它是一个标准,所以它不是“完全非标准”。我支持你,但这将是我们的意见。
LeonelGalán13年

在什么时候?链接的文章(通过Wayback或alt URL)说,SQL:1999和SQL:2003都对GROUP BYMySQL忽略的内容施加了限制。
IMSoP

1

对于其他寻求在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")

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.