4

テーブル内の各ユーザーの SUM() をフェッチしようとしていますが、MySQL が間違った値を返しています。

これがどのように見えるかです(http://sqlfiddle.com/#!2/7b988/4/0

user    amount
110     20.898319244385
114     43.144836425781
115     20.487638473511
116     26.07483291626
117     93.054000854492

しかし、これはどのように見えるかです(http://sqlfiddle.com/#!2/7b988/2/0

user    amount
110     167.186554
114     129.434509
115     143.413469
116     208.598663
117     744.432007

これは私が実行しようとしているクエリです:

SELECT 
    blocks.user_id, 
    SUM(payout_history.amount) as amount
FROM blocks
LEFT JOIN payout_history
ON blocks.user_id = payout_history.user_id
WHERE confirms > 520
GROUP BY blocks.user_id

私は何を間違っていますか?

4

2 に答える 2

9

このクエリを試してください:

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各 の列の合計を計算する必要があるようですが、テーブルにもあり、520 よりも大きい値を持つuser_idもののみです 。単純な結合 (左外部結合も) は機能しませんこの場合、テーブルには同じ の多くのレコードが含まれる可能性があるためです。たとえば、行のみを返すクエリは次の結果を返します。user_idblocksblocks.confirms
blocksuser_iduser_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
;
于 2013-08-25T15:31:04.837 に答える