2009-11-20 20 views
64

He estado migrando algunas de mis consultas MySQL a PostgreSQL para usar Heroku. La mayoría de mis consultas funcionan correctamente, pero sigo teniendo un error recurrente similar cuando utilizo grupo por:GRUPO PostgreSQL POR diferente de MySQL?

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

Podría alguien decirme lo que estoy haciendo mal?


MySQL que funciona al 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 error:

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


código Ruby generar el 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') 


Resultados esperados (de trabajar con MySQL Query):

 
+-----+-------+-------+------------+---------+---------------+---------------+ 
| 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

tan ... ¿Me sería más útil usar la función distinta en bookdate? Si lo hiciera, ¿aún necesitaría la cláusula group by? – holden

+2

'DISTINCT' es más lento que' GROUP BY'. Por lo tanto, debe tener cuidado y preferir una solución 'GROUP BY' si es posible. – Franz

Respuesta

103

Cumple totalmente con los estándares de MySQL GROUP BY puede ser emulado por Postgres 'DISTINCT ON. Considere esto:

mysql:

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

Esto ofrece 1 fila por cada valor de a (los cuales uno, no se sabe muy bien). Bueno, en realidad se puede adivinar, porque MySQL no sabe acerca de los agregados hash, por lo que probablemente utilizará un género ... pero solo ordenará en a, por lo que el orden de las filas podría ser aleatorio. A menos que use un índice de columnas múltiples en lugar de ordenar. Bueno, de todos modos, no está especificado por la consulta.

postgres:

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

Esto ofrece 1 fila por cada valor de a, esta fila será el primero en la clase según la ORDER BY especificada por la consulta. Sencillo.

Tenga en cuenta que aquí, no es un agregado que estoy computando.Entonces GROUP BY en realidad no tiene sentido. DISTINCT ON tiene mucho más sentido.

Rails está casado con MySQL, así que no me sorprende que genere SQL que no funcione en postgres.

+6

Agregando a esto, sin embargo, Postgres 9.1 permite no enumerar todas las columnas si la clave primaria de su tabla es parte de la cláusula 'group by'. –

+4

De acuerdo con [este artículo "Desmitificar los mitos de GROUP BY"] (http://rpbouman.blogspot.se/2007/05/debunking-group-by-myths.html), no tiene nada que ver con "GRUPO que no cumple con los estándares" POR". – Rafa

+4

Según este artículo, GROUP BY de MySQL todavía no cumple con las dos versiones del estándar, porque no verifica si las columnas adicionales en la lista de selección dependen del grupo por columnas. Emitirá datos incorrectos sin previo aviso (pero también puede servir para fines útiles). PG 9.1 asume que incluir el PK de una tabla significa que todas las otras columnas son dependientes, lo cual es correcto. Esto no cubre el estándar 100% (otras consultas correctas pueden marcarse como errores) pero cubre la mayoría de los casos de uso sin devolver resultados incorrectos ... – peufeu

8

de MySQL GROUP BY puede utilizarse sin una función de agregado (lo cual es contrario al estándar SQL), y devuelve la primera fila en el grupo (I Don 't saber en función de qué criterios), mientras que PostgreSQL debe tener una función agregada (MAX, SUM, etc.) en la columna, en la que se emite la cláusula GROUP BY.

3

Si mal no recuerdo, en PostgreSQL debe agregar cada columna que obtenga de la tabla donde la cláusula GROUP BY aplica a la cláusula GROUP BY.

16

PostgreSQL es más compatible con SQL que MySQL. Todos los campos, excepto el campo calculado con función de agregación, en el resultado deben estar presentes en la cláusula GROUP BY.

4

Correcto, la solución para solucionar esto es usar: seleccionar y seleccionar cada campo con el que desea decorar el objeto resultante y agruparlo.

Desagradable, pero es cómo el grupo debería funcionar en lugar de cómo funciona MySQL al adivinar lo que quiere decir si no se pegan campos en su grupo.

+1

Supongo que MySQL me ha echado a perder, o me ha arruinado, cualquiera que sea el adjetivo que prefiera, ¿así que no hay mejor manera? Es decir. arrojar una función agregada como MAX (bookdate) o DISTINCT que me dijeron anteriormente es mucho más lenta? – holden

+0

Me quedaría con el grupo por, pero pise con cuidado, especialmente porque tiene que seleccionar manualmente los campos con los que desea decorar el objeto. También escribir el manual select with group by es un enfoque más independiente de la base de datos, teniendo en cuenta que MSSQL (si tiene la mala suerte de tener que usarlo) y Oracle también se quejará de manera similar. –

+0

DISTINCT no significa necesariamente más lento. – nos

1

Según MySQL "Debuking GROUP BY Myths" http://dev.mysql.com/tech-resources/articles/debunking-group-by-myths.html. SQL (versión 2003 del estándar) no requiere que las columnas a las que se hace referencia en la lista SELECCIONAR de una consulta aparezcan también en la cláusula GROUP BY.

+1

Pero, como han señalado otros, sí requiere que sean "funcionalmente dependientes" de las columnas que * están * en el 'GROUP BY '. La capacidad de MySQL para hacer referencia a * cualquier * columna no agrupada es completamente no estándar, y permite a los usuarios escribir consultas ilógicas y no confiables. – IMSoP

+0

Era un estándar en ese momento, por lo que no es "totalmente no estándar". Estoy de tu lado, pero esa será nuestra opinión. – Leito

+0

¿A qué hora? El artículo vinculado (a través de Wayback, o [alt URL] (http://rpbouman.blogspot.se/2007/05/debunking-group-by-myths.html)) dice que tanto SQL: 1999 como SQL: 2003 imponen límites en 'GROUP BY' que MySQL ignora. – IMSoP

2
No

la solución más bonita, pero cambiando el parámetro de grupo a la salida de cada columna en el modelo funciona en 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

Para otros en busca de una forma de orden por cualquier campo, incluido el campo unido, en postgresql, use una subconsulta:

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") 
Cuestiones relacionadas