sql >> Databasteknik >  >> RDS >> Mysql

MySQL:SUM() med JOIN returnerar felaktiga värden

Testa den här frågan:

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 förklaring av hur frågan fungerar (eller snarare varför din fråga inte fungerar) ----

Om man tittar på förväntade resultat verkar det som att frågan bör beräkna summan av amount kolumn för varje user_id , men bara för dessa user_id , som också finns i blocks tabell och har en blocks.confirms värde större än 520.
En enkel koppling (även vänster yttre koppling) kan inte fungera i det här fallet, eftersom blocks Tabell kan innehålla många poster för samma user_id , till exempel en fråga som returnerar rader för endast user_id=110 ger följande resultat:

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 raka kopplingen (och LEFT/RIGHT yttre kopplingen) fungerar på det här sättet, som tar varje post från den första sammanfogade tabellen, och para denna post (kombinera den) med alla rader från den andra sammanfogade tabellen som uppfyller kopplingsvillkoret.

I vårt fall ger den vänstra kopplingen följande resultat:

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

och nu om vi lägger till SUM( amount ) .... GROUP BY user_id , kommer MySql att beräkna summan av alla amount värden från ovanstående resultatuppsättning ( 8 rader * 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 det här fallet ger anslutningen inte önskat resultat - vi behöver något som heter a semi join - nedan är olika varianter av semi joins, prova 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. Gör en videodelningssida, behöver en videospelare

  2. Matcha 2 sql-kolumner if =uppdatera sedan en annan kolumn med 1

  3. Hur man beviljar åtkomst till v$-visningar (v$session ,v$instance)

  4. MySQL ORDER BY DESC är snabb men ASC är väldigt långsam