Mysql
 sql >> база данни >  >> RDS >> Mysql

MySQL:SUM() с JOIN връща неправилни стойности

Опитайте тази заявка:

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



--- РЕДАКТИРАНЕ --- обяснение как работи заявката (или по-скоро защо вашата заявка не работи) ----

Разглеждайки очакваните резултати изглежда, че заявката трябва да изчисли сума от amount колона за всеки user_id , но само за тези user_id , които също са в blocks таблица и има blocks.confirms стойност по-голяма от 520.
Просто присъединяване (също ляво външно съединение) не може да работи в този случай, тъй като blocks таблицата може да съдържа много записи за един и същ user_id , например заявка, която връща редове само за user_id=110 дава следните резултати:

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

Правото свързване (и външното LEFT/RIGHT) работи по този начин, което взема всеки запис от първата обединена таблица и сдвоява този запис (комбинира го) с всички редове от другата обединена таблица, които отговарят на условието за свързване.

В нашия случай лявото присъединяване произвежда по-долу набор от резултати:

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

и сега, ако добавим SUM( amount ) .... GROUP BY user_id , MySql ще изчисли сумата от цялата amount стойности от горния набор от резултати ( 8 реда * 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        |
+ ------------ + ----------------- +



Както виждате в този случай присъединяването не ни дава желаните резултати - трябва ни нещо с име a semi join - по-долу са различни варианти на полусъединяване, опитайте ги:

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. Как да задам избрания елемент в падащо меню

  2. Търсене в близост

  3. Как да добавя условия в подподподчинените дъщерни модели в sequelize, които трябва да повлияят на моя родителски модел в findAndCountAll?

  4. lower_case_table_names=1 на Ubuntu 18.04 не позволява на mysql да стартира

  5. Проверка на таблица за припокриване на времето?