sql >> Database teknologi >  >> RDS >> Mysql

MySQL:SUM() med JOIN returnerer forkerte værdier

Prøv denne forespørgsel:

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 --- en forklaring på, hvordan forespørgslen fungerer (eller rettere hvorfor din forespørgsel ikke virker) ----

Ser man på forventede resultater, ser det ud til, at forespørgslen skal beregne en sum af amount kolonne for hver user_id , men kun for disse user_id , som også er i blocks tabel, og har en blocks.confirms værdi større end 520.
En simpel joinforbindelse (også venstre ydre joinforbindelse) kan ikke fungere i dette tilfælde, fordi blocks tabel kan indeholde mange poster for det samme user_id , for eksempel en forespørgsel, der kun returnerer rækker for user_id=110 giver følgende resultater:

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

Den lige joinforbindelse (og LEFT/RIGHT ydre joinforbindelse) fungerer på denne måde, der tager hver post fra den første sammenføjede tabel, og parrer denne post (kombiner den) med alle rækker fra den anden sammenføjede tabel, der opfylder joinbetingelsen.

I vores tilfælde producerer venstre joinforbindelse et nedenstående resultatsæt:

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

og nu hvis vi tilføjer SUM( amount ) .... GROUP BY user_id , vil MySql beregne en sum af alt amount værdier fra ovenstående resultatsæt (8 rækker * 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        |
+ ------------ + ----------------- +



Som du ser i dette tilfælde giver joinforbindelsen os ikke de ønskede resultater - vi har brug for noget der hedder a semi join - nedenfor er forskellige varianter af semi joins, prøv dem:

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. Brug af backticks omkring feltnavne

  2. Problem ved hentning af poster med tomt array

  3. Vælg SQL Server-databasestørrelse

  4. Hvordan kan jeg lave boolesk logik på to kolonner i MySQL?