sql >> Databáze >  >> RDS >> Mysql

MySQL:SUM() s JOIN vrací nesprávné hodnoty

Zkuste tento dotaz:

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



--- UPRAVIT --- vysvětlení, jak dotaz funguje (nebo spíše proč váš dotaz nefunguje) ----

Při pohledu na očekávané výsledky se zdá, že dotaz by měl vypočítat součet amount sloupec pro každý user_id , ale pouze pro user_id , které jsou také v blocks tabulku a mají blocks.confirms hodnota větší než 520.
Jednoduché spojení (také levé vnější spojení) v tomto případě nemůže fungovat, protože blocks tabulka může obsahovat mnoho záznamů pro stejné user_id , například dotaz, který vrací řádky pouze pro user_id=110 dává následující výsledky:

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

Tímto způsobem funguje přímé spojení (a LEFT/RIGHT vnější spojení), které vezme každý záznam z první spojené tabulky a spáruje tento záznam (zkombinuje jej) se všemi řádky z druhé spojené tabulky, pokud splňují podmínku spojení.

V našem případě levé spojení vytváří níže uvedenou sadu výsledků:

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 teď, když přidáme SUM( amount ) .... GROUP BY user_id , MySql vypočítá součet všech amount hodnoty z výše uvedené sady výsledků ( 8 řádků * 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 vidíte v tomto případě, spojení nám nedává požadované výsledky – potřebujeme něco s názvem a semi join - níže jsou různé varianty semi spojení, vyzkoušejte 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. Syntaxe zástupných symbolů Python MySQLdb

  2. Export databáze SQLite do souboru XML

  3. Víceřadá vložka s pg-promise

  4. NodeJS mySQL Insert Blob