Mysql
 sql >> Baza danych >  >> RDS >> Mysql

MySQL:SUM() z JOIN zwraca nieprawidłowe wartości

Wypróbuj to zapytanie:

SELECT bl.user_id, SUM( ph.amount ) PAIDOUT
FROM (
   SELECT distinct blocks.user_id 
   FROM blocks
   WHERE confirms > 520
) bl
LEFT JOIN  payout_history ph
ON bl.user_id = ph.user_id
GROUP BY ph.user_id
;

SQLFiddle --> http://sqlfiddle.com/#!2/7b988/48



--- EDIT --- wyjaśnienie, jak działa zapytanie (a raczej dlaczego zapytanie nie działa) ----

Patrząc na oczekiwane wyniki wydaje się, że zapytanie powinno obliczyć sumę amount kolumna dla każdego user_id , ale tylko dla tych user_id , które również znajdują się w blocks tabeli i mieć blocks.confirms wartość większa niż 520.
Proste łączenie (również lewe łączenie zewnętrzne) nie może działać w tym przypadku, ponieważ blocks tabela może zawierać wiele rekordów dla tego samego user_id , na przykład zapytanie, które zwraca wiersze tylko dla user_id=110 daje następujące wyniki:

SELECT *
FROM blocks
WHERE confirms > 520
      AND user_id = 110;

+ ------- + ------------ + ----------- + ------------- +
| id      | user_id      | reward      | confirms      |
+ ------- + ------------ + ----------- + ------------- +
| 0       | 110          | 20.89832115 | 521           |
| 65174   | 110          | 3.80357075  | 698           |
| 65204   | 110          | 4.41933060  | 668           |
| 65218   | 110          | 4.69059801  | 654           |
| 65219   | 110          | 4.70222521  | 653           |
| 65230   | 110          | 4.82805490  | 642           |
| 65265   | 110          | 5.25058079  | 607           |
| 65316   | 110          | 6.17262650  | 556           |
+ ------- + ------------ + ----------- + ------------- +

Łączenie proste (i łączenie zewnętrzne LEWE/PRAWE) działa w ten sposób, że każdy rekord z pierwszej połączonej tabeli łączy w parę (łączy) ze wszystkimi wierszami z drugiej połączonej tabeli, które spełniają warunek złączenia.

W naszym przypadku lewe złącze daje następujący zestaw wyników:

SELECT *
FROM blocks
LEFT JOIN payout_history
ON blocks.user_id = payout_history.user_id
WHERE confirms > 520
    AND blocks.user_id = 110;
+ ------- + ------- + ----------- + -------- + --- + ------- + ----------- +
| id      | user_id | reward      | confirms | id  | user_id | amount      |
+ ------- + ------- + ----------- + -------- + --- + ------- + ----------- +
| 0       | 110     | 20.89832115 | 521      | 1   | 110     | 20.898319   |
| 65174   | 110     | 3.80357075  | 698      | 1   | 110     | 20.898319   |
| 65204   | 110     | 4.41933060  | 668      | 1   | 110     | 20.898319   |
| 65218   | 110     | 4.69059801  | 654      | 1   | 110     | 20.898319   |
| 65219   | 110     | 4.70222521  | 653      | 1   | 110     | 20.898319   |
| 65230   | 110     | 4.82805490  | 642      | 1   | 110     | 20.898319   |
| 65265   | 110     | 5.25058079  | 607      | 1   | 110     | 20.898319   |
| 65316   | 110     | 6.17262650  | 556      | 1   | 110     | 20.898319   |
+ ------- + ------- + ----------- + -------- + --- + ------- + ----------- +

a teraz jeśli dodamy SUM( amount ) .... GROUP BY user_id , MySql obliczy sumę wszystkich amount wartości z powyższego zestawu wyników (8 wierszy * 20,898 =~ 167,184)

SELECT blocks.user_id, sum( amount)
FROM blocks
LEFT JOIN payout_history
ON blocks.user_id = payout_history.user_id
WHERE confirms > 520
    AND blocks.user_id = 110
GROUP BY blocks.user_id;
+ ------------ + ----------------- +
| user_id      | sum( amount)      |
+ ------------ + ----------------- +
| 110          | 167.186554        |
+ ------------ + ----------------- +



Jak widać w tym przypadku łączenie nie daje pożądanych rezultatów - potrzebujemy czegoś o nazwie a semi join - poniżej różne warianty semijoin, wypróbuj je:

SELECT bl.user_id, SUM( ph.amount ) PAIDOUT
FROM (
   SELECT distinct blocks.user_id 
   FROM blocks
   WHERE confirms > 520
) bl
LEFT JOIN  payout_history ph
ON bl.user_id = ph.user_id
GROUP BY ph.user_id
;


SELECT ph.user_id, SUM( ph.amount ) PAIDOUT
FROM payout_history ph
WHERE ph.user_id IN (
     SELECT user_id FROM blocks
     WHERE confirms > 520
  )
GROUP BY ph.user_id
;

SELECT ph.user_id, SUM( ph.amount ) PAIDOUT
FROM payout_history ph
WHERE EXISTS (
     SELECT 1 FROM blocks bl
     WHERE bl.user_id = ph.user_id
        AND bl.confirms > 520
  )
GROUP BY ph.user_id
;


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. WYBIERZ * Z wielu tabel. MySQL

  2. UNIKALNE OGRANICZENIE MYSQL

  3. O kursorze i iteratorze mysql

  4. Błąd MYSQL:1045 (28000):Odmowa dostępu dla użytkownika „root”@„localhost”

  5. Schemat bazy danych Laravel, wartość nullable Foreign