2010-07-23 27 views
13

Tengo dos tablas en MySQL 5.1.38.Obtenga SUM en GROUP BY con JOIN usando MySQL

products 
+----+------------+-------+------------+ 
| id | name  | price | department | 
+----+------------+-------+------------+ 
| 1 | Fire Truck | 15.00 | Toys  | 
| 2 | Bike  | 75.00 | Toys  | 
| 3 | T-Shirt | 18.00 | Clothes | 
| 4 | Skirt  | 18.00 | Clothes | 
| 5 | Pants  | 22.00 | Clothes | 
+----+------------+-------+------------+ 

ratings 
+------------+--------+ 
| product_id | rating | 
+------------+--------+ 
|   1 |  5 | 
|   2 |  5 | 
|   2 |  3 | 
|   2 |  5 | 
|   3 |  5 | 
|   4 |  5 | 
|   5 |  4 | 
+------------+--------+ 

Mi objetivo es obtener el precio total de todos los productos que tienen una calificación de 5 estrellas en cada departamento. Algo como esto.

+------------+-------------+ 
| department | total_price | 
+------------+-------------+ 
| Clothes | 36.00  | /* T-Shirt and Skirt */ 
| Toys  | 90.00  | /* Fire Truck and Bike */ 
+------------+-------------+ 

Me gustaría hacer esto sin una subconsulta si puedo. Al principio intenté unirme con una suma().

select department, sum(price) from products 
join ratings on product_id=products.id 
where rating=5 group by department; 
+------------+------------+ 
| department | sum(price) | 
+------------+------------+ 
| Clothes |  36.00 | 
| Toys  |  165.00 | 
+------------+------------+ 

Como se puede ver el precio para el departamento de juguetes es incorrecto, porque hay dos clasificaciones de 5 estrellas para la moto y, por tanto, contando que el precio dos veces debido a la unión.

Intenté agregar distinto a la suma.

select department, sum(distinct price) from products 
join ratings on product_id=products.id where rating=5 
group by department; 
+------------+---------------------+ 
| department | sum(distinct price) | 
+------------+---------------------+ 
| Clothes |    18.00 | 
| Toys  |    90.00 | 
+------------+---------------------+ 

Pero el departamento de ropa está apagado porque dos productos comparten el mismo precio.

Actualmente mi solución consiste en tomar algo único sobre el producto (la identificación) y utilizarlo para que el precio sea único.

select department, sum(distinct price + id * 100000) - sum(id * 100000) as total_price 
from products join ratings on product_id=products.id 
where rating=5 group by department; 
+------------+-------------+ 
| department | total_price | 
+------------+-------------+ 
| Clothes |  36.00 | 
| Toys  |  90.00 | 
+------------+-------------+ 

Pero esto se siente como un truco tan tonto. ¿Hay una mejor manera de hacer esto sin una subconsulta? ¡Gracias!

+2

¿Qué tienes contra subconsultas? –

+0

Mis combinaciones y condiciones son más complejas y dinámicas, y mi ORM (Active Record) no admite bien las subconsultas. – ryanb

+0

¿Cómo sabes desde la segunda mesa a qué departamento pertenece la calificación? –

Respuesta

16

uso:

SELECT p.department, 
     SUM(p.price) AS total_price 
    FROM PRODUCTS p 
    JOIN (SELECT DISTINCT 
       r.product_id, 
       r.rating 
      FROM RATINGS r) x ON x.product_id = p.id 
          AND x.rating = 5 
GROUP BY p.department 

Técnicamente, esto no utiliza una subconsulta - que utiliza una tabla/línea derivada vie w.

Marcando esto como wiki de la comunidad porque algunos monos me siguen bailando aunque es 100% correcto.

+5

Spank the monkey – Anax

+0

Gracias OMG Ponies! Esto resolvió perfectamente un problema que estaba teniendo hoy. Mi caso específico requirió una UNIÓN IZQUIERDA en la tabla derivada y tener la SUMA dentro de la definición de la tabla derivada, pero funciona muy bien. Los resultados de EXPLAIN tampoco se veían demasiado horribles, así que veremos cómo se amplía. –

+0

¡Esta solución hizo mi día! :) – jirislav

-1

Puede hacer dos consultas. Primera consulta:

 
SELECT DISTINCT product_id FROM ratings WHERE rating = 5; 

Entonces, toma cada una de esas identificaciones y de forma manual los puso en la segunda consulta:

 
SELECT department, Sum(price) AS total_price 
FROM  products 
WHERE product_id In (1,2,3,4) 
GROUP BY department; 

Este es el trabajo en torno a no ser capaz de utilizar subconsultas. Sin ellos, no hay forma de eliminar los registros duplicados causados ​​por la unión.

-1

No puedo pensar en ninguna forma de hacerlo sin una subconsulta en algún lugar en la consulta. Quizás podría usar una Vista para enmascarar el uso de una subconsulta.

Al margen de eso, la mejor opción es encontrar el conjunto de datos mínimo necesario para hacer el cálculo y hacerlo en el frente. Si eso es posible o no depende de sus datos específicos: cuántas filas, etc.

La otra opción (de hecho, quizás sea la mejor ...) sería obtener un nuevo ORM o prescindir del mismo;)

este punto de vista le permitirá eludir la subconsulta:

CREATE VIEW Distinct_Product_Ratings 
AS 
    SELECT DISTINCT 
     product_id, 
     rating 
    FROM 
     Ratings 
0

La razón principal por la que tiene problemas para encontrar una solución es que el esquema presentado es fundamentalmente defectuoso. No debe permitir que una tabla tenga dos filas que sean duplicados completos entre sí. Cada tabla debe tener un medio para identificar de manera única cada fila, incluso si es la combinación de todas las columnas. Ahora, si cambiamos la mesa ratings de modo que tenga una columna llamada AUTO_INCREMENTId, el problema es más fácil:

Select products.department, Sum(price) As total_price 
From products 
    Left Join ratings As R1 
     On R1.product_id = products.id 
      And R1.rating = 5 
    Left Join ratings As R2 
     On R2.product_id = R1.product_id 
      And R2.rating = R1.rating 
      And R2.Id > R1.Id 
Where R2.Id Is Null 
Group By products.department 
+0

En realidad, tengo un campo de identificación autoincrementable en la aplicación real, que es mucho más complejo. Traté de simplificar todo lo que pude aquí, pero parece que fui demasiado lejos al sacar ratings.id. ¡Gracias por publicar esta opción! – ryanb