2009-11-20 144 views
64

我一直在将一些MySQL查询迁移到PostgreSQL来使用Heroku。我的大部分的查询做工精细,但我一直有类似的重复错误,当我通过使用组:PostgreSQL GROUP BY与MySQL不同?

ERROR: column "XYZ" must appear in the GROUP BY clause or be used in an aggregate function

有人能告诉我什么,我做错了什么?


的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: ERROR: column "availables.id" must appear in the GROUP BY clause or be used in an aggregate function:
SELECT "availables".* FROM "availables" INNER JOIN "rooms" ON "rooms".id = "availables".room_id WHERE (rooms.hotel_id = 5056 AND availables.bookdate BETWEEN E'2009-10-21' AND E'2009-10-23') GROUP BY availables.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 
+0

sooo ...我会更好地使用bookdate上的独特功能吗?如果我这样做了,我是否仍然需要group by子句? – holden 2009-11-20 10:07:35

+2

'DISTINCT'比'GROUP BY'慢。所以你应该小心,如果可能的话,更喜欢'GROUP BY'解决方案。 – Franz 2009-11-20 11:10:08

回答

103

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行,该行会根据ORDER BY通过查询中指定在排序的第一个。简单。

请注意,在这里,它不是我计算的总和。所以GROUP BY实际上没有意义。 DISTINCT ON更有意义。

Rails与MySQL结婚,所以我不惊讶它生成的SQL在postgres中不起作用。

+6

除此之外,Postgres 9.1允许不列出所有列,如果他们的表的主键是'group by'子句的一部分。 – 2011-06-02 15:59:48

+4

根据[这篇文章“Debunking GROUP BY myths”](http://rpbouman.blogspot.se/2007/05/debunking-group-by-myths.html),它与“不符合标准的GROUP通过”。 – Rafa 2012-08-08 16:26:46

+4

根据这篇文章,MySQL的GROUP BY仍然不符合标准的两个版本,因为它不验证选择列表中的额外列是否依赖于按列分组。它会在没有警告的情况下输出错误的数据(但也可以用于有用的目的)。 PG 9.1假定包含表的PK意味着所有其他列都是依赖的,这是正确的。这不包括标准的100%(其他正确的查询可能被标记为错误),但涵盖大多数使用情况,而不会返回错误的结果...... – peufeu 2012-08-18 09:15:12

8

MySQL的GROUP BY可以在没有聚合函数(这是违反SQL标准)使用,而该组中返回的第一行(我不不知道基于什么标准),而PostgreSQL必须在发布GROUP BY子句的列上有一个聚合函数(MAX,SUM等)。

3

如果我没有记错,在PostgreSQL中,您必须添加从GROUP BY子句应用 GROUP BY子句的表中获取的每一列。

16

PostgreSQL比MySQL更符合SQL标准。输出中的所有字段(除了具有聚合函数的计算字段)都必须存在于GROUP BY子句中。

4

正确的,解决这个问题的方法是使用:select和选择每个字段,你希望用它们来装饰生成的对象。

讨厌 - 但它是如何通过应该工作,而不是MySQL如何与它一起工作,猜测你的意思,如果你不坚持你的小组领域。

+1

我想MySQL已经宠坏了我,或者毁了我,无论你喜欢哪个形容词,所以没有更好的办法了? IE浏览器。抛出一个像MAX(bookdate)或DISTINCT这样的集合函数,上面告诉我的是慢得多的? – holden 2009-11-20 11:33:34

+0

我会坚持团体的 - 但要小心,特别是因为你必须手动选择你想装饰对象的领域。 另外写手册选择与组是一个更多的数据库不可知论的方法,考虑到MSSQL(如果你不幸得不得不使用它)和甲骨文也会以类似的方式抱怨。 – 2009-11-21 00:15:10

+0

DISTINCT不一定意味着更慢。 – nos 2009-11-22 01:40:46

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引用* any * non-grouped列的能力完全是非标准的,并且允许用户编写不合逻辑和不可靠的查询。 – IMSoP 2013-04-15 19:49:27

+0

这是当时的标准,所以它不是“完全不标准的”。我与你同在,但那将是我们的看法。 – Leito 2013-04-15 20:30:24

+0

什么时间?链接的文章(通过Wayback或[alt URL](http://rpbouman.blogspot.se/2007/05/debunking-group-by-myths.html))说,SQL:1999和SQL:2003都会限制关于MySQL忽略的GROUP BY。 – IMSoP 2013-04-15 21:23:34

2

不是最漂亮的解决方案,但改变组参数输出模型中的每个列工作在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

对于其他人寻找一种方式来在任何领域,包括加入领域,在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")