2010-07-23 23 views
13

Mam dwie tabele w MySQL 5.1.38.Suma w GROUP BY przy JOIN przy użyciu 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 | 
+------------+--------+ 

Mój cel to uzyskanie całkowitej ceny wszystkich produktów, które mają 5 gwiazdek w każdym dziale. Coś takiego.

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

Chciałbym zrobić to bez podzapytania, jeśli mogę. Na początku próbowałem połączyć z sumą().

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

Jak widać cenę za dział zabawek jest błędna, ponieważ istnieją dwa wskaźniki 5-gwiazdkowe na rower i dlatego liczy, że cena dwukrotnie z powodu łączenia.

Potem próbowałem dodawać odrębne do sumy.

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

Ale wtedy dział ubrań jest wyłączony, ponieważ dwa produkty mają tę samą cenę.

Obecnie moja praca polega na zrobieniu czegoś wyjątkowego na temat produktu (identyfikatora) i wykorzystaniu go, aby cena była unikalna.

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

Ale to wygląda jak głupi hack. Czy jest lepszy sposób to zrobić bez podzapytania? Dzięki!

+2

Co masz przeciwko podzapytania? –

+0

Moje połączenia i warunki są bardziej złożone i dynamiczne, a moje ORM (rekord aktywny) nie obsługuje dobrze podkwerend. – ryanb

+0

Skąd wiesz od drugiej tabeli, do której działu wystawia się ocena? –

Odpowiedz

16

Zastosowanie:

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 

Technicznie rzecz biorąc, to nie używa podzapytania - używa pochodzący stołu/inline vie w.

Oznaczenie tej strony jako społecznościowej wiki, ponieważ niektóre małpy ignorują mnie, chociaż jest to w 100% poprawne.

+5

Spank the monkey – Anax

+0

Dzięki Kucyki OMG! To doskonale rozwiązało problem, który miałem dzisiaj. Mój szczególny przypadek wymagał LEFT JOIN na wyprowadzonej tabeli i miał SUM wewnątrz definicji tabeli pochodnej, ale działa wspaniale. Wyniki EXPLAIN również nie wyglądały zbyt strasznie, więc zobaczymy, jak się skaluje. –

+0

To rozwiązanie uczyniło mój dzień! :) – jirislav

-1

Możesz wykonać dwa zapytania. Pierwsze zapytanie:

 
SELECT DISTINCT product_id FROM ratings WHERE rating = 5; 

Następnie podejmuje każdy z tych identyfikatorami i ręcznie umieścić je w drugim zapytaniu:

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

To obejście nie jest w stanie wykorzystać podzapytania. Bez nich nie ma sposobu na wyeliminowanie duplikatów rekordów spowodowanych przez dołączenie.

-1

Nie mogę wymyślić żadnego sposobu, aby to zrobić bez podzapytania gdzieś w zapytaniu. Możliwe, że użyjesz widoku do zamaskowania użycia podzapytania.

Po pierwsze, najlepiej jest znaleźć minimalny zestaw danych potrzebny do wykonania obliczeń i zrobić to z przodu. To, czy jest to możliwe, zależy od twoich danych - ile rzędów, itp.

Inną opcją (a może jest to najlepsza ...) byłoby zdobycie nowego ORMa lub obejść się bez niego;)

pogląd ten pozwoli Ci ominąć podzapytania:

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

Głównym powodem, dla którego masz problem ze znalezieniem rozwiązania, jest to, że przedstawiony schemat jest zasadniczo wadliwy. Nie należy zezwalać, aby tabela zawierała dwa wiersze, które są pełnymi duplikatami. Każda tabela powinna mieć możliwość unikalnego identyfikowania każdego wiersza, nawet jeśli jest to połączenie wszystkich kolumn. Teraz, jeśli możemy zmienić tabelę ratings tak, że ma kolumnę AUTO_INCREMENT nazwie Id, problem jest łatwiej:

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

Właściwie, mam automatycznie zwiększane pole id w prawdziwej aplikacji, które jest znacznie bardziej złożone. Starałem się uprościć wszystko, o ile mogłem, ale wydaje mi się, że posunąłem się za daleko, usuwając ratings.id. Dziękujemy za opublikowanie tej opcji! – ryanb

Powiązane problemy