sql >> Database >  >> RDS >> Mysql

MySQL:SUM() met JOIN retourneert onjuiste waarden

Probeer deze zoekopdracht:

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 --- een uitleg hoe de query werkt (of beter gezegd waarom uw query niet werkt) ----

Als we naar de verwachte resultaten kijken, lijkt het erop dat de zoekopdracht een som moet berekenen van amount kolom voor elke user_id , maar alleen voor die user_id , die ook in de blocks . staan tabel, en hebben een blocks.confirms waarde groter dan 520.
Een simpele join (ook left outer join) kan in dit geval niet werken, omdat de blocks tabel kan veel records bevatten voor dezelfde user_id , bijvoorbeeld een query die alleen rijen retourneert voor user_id=110 geeft de volgende resultaten:

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

De straigh join (en de LEFT/RIGHT outer join) werkt op deze manier, waarbij elk record uit de eerste samengevoegde tabel wordt genomen en dit record wordt gekoppeld (combineert) met alle rijen van de andere samengevoegde tabel om aan de join-voorwaarde te voldoen.

In ons geval levert de left join een onderstaande resultatenset op:

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

en nu als we SUM( amount ) .... GROUP BY user_id , MySql berekent een som van alle amount waarden uit de bovenstaande resultatenset (8 rijen * 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        |
+ ------------ + ----------------- +



Zoals je ziet in dit geval geeft de join ons niet de gewenste resultaten - we hebben iets nodig met de naam a semi join - hieronder staan ​​verschillende varianten van semi-joins, probeer ze eens:

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. Hoe de maxrecursion-optie voor een CTE in een Table-Valued-Function in te stellen?

  2. Een tijdelijke oplossing voor DATEDIFF() SET DATEFIRST negeren in SQL Server (T-SQL-voorbeeld)

  3. Slaapstand codeert verkeerd tijdens persistente objecten [UTF-8]

  4. Hoe beïnvloeden IMMUTABLE, STABLE en VOLATILE trefwoorden het gedrag van de functie?