2016-08-17 18 views
1

3つのテーブル。Mysqlの左結合が期待通りに機能しない

table_customers - customer_id, name 
table_orders - order_id, customer_id, order_datetime 
table_wallet - customer_id, amount, type // type 1- credit, type 2- debit 

すべてのお客様、合計残高、および最終注文日と注文IDを取得する必要があります。お客様が0000-00-00、返品オーダーIDを0に設定していない場合、

これは私の質問です。

SELECT 
C.customer_id, 
C.name, 
COALESCE(SUM(CASE WHEN type = 2 THEN -W.amount ELSE W.amount END), 0) AS value, 
COALESCE(max(O.order_id ) , '0') AS last_order_id, 
COALESCE(max(date(O.order_datetime)) , '0000-00-00') AS last_order_date 
FROM 
table_customers as C 
LEFT JOIN 
table_wallet as W 
ON C.customer_id = W.customer_id 
LEFT JOIN 
table_orders AS O 
ON W.customer_id = O.customer_id 
group by C.customer_id 
ORDER BY C.customer_id 

お客様の価値を除いてすべてが正しいとされています。結果から、複数回追加されているようです。

私はここでフィーバーを作成しました。 http://sqlfiddle.com/#!9/560f2/1

クエリで何が間違っていますか?誰かが私にこれを助けることができますか?

編集:期待される結果

customer_id name value last_order_id  last_order_date 
    1   abc  20  3    2016-06-22 
    2   def  112.55 0    0000-00-00 
    3   pqrs  0  4    2016-06-15 
    4   wxyz  0  0    0000-00-00 
+0

が私に正しく見える量の顧客ID 1の重複。明確にするために期待される結果を追加したいですか? – fancyPants

+0

@fancyPantsは、問題の予想される結果を追加しました。 –

答えて

2

問題あなたは本当にただの順序から財布ごとに1行をしたい各財布の受注、としてそこだけ多くの行を生成します注文と財布の間の結合ということですテーブル(最大値のみを使用するため)。あなたのテストケースでは、合計1(3 * 20)を作る顧客1に対して3行が得られます。これを解決する

一つの方法は、これに変更することです:

SELECT 
    C.customer_id, 
    C.name, 
    COALESCE(SUM(CASE WHEN type = 2 THEN -W.amount ELSE W.amount END), 0) AS value, 
    COALESCE(O.order_id , '0') AS last_order_id, 
    COALESCE(DATE(O.order_datetime) , '0000-00-00') AS last_order_date 
FROM table_customers AS C 
LEFT JOIN table_wallet AS W ON C.customer_id = W.customer_id 
LEFT JOIN (
    SELECT 
    customer_id, 
    MAX(order_id) AS order_id, 
    MAX(order_datetime) AS order_datetime 
    FROM table_orders 
    GROUP BY customer_id 
) AS O ON c.customer_id = O.customer_id 
GROUP BY C.customer_id 
ORDER BY C.customer_id 

あなたが注文テーブルはあなたに、顧客ごとに1行を取得し、派生テーブルに置き換えられて見ての通り。

query aboveを実行すると、あなたの次の結果を取得します:

| customer_id | name | value | last_order_id | last_order_date | 
|-------------|------|--------|---------------|-----------------| 
|   1 | abc |  20 |    3 |  2016-06-22 | 
|   2 | def | 112.55 |    0 |  0000-00-00 | 
|   3 | pqrs |  0 |    4 |  2016-06-15 | 
|   4 | wxyz |  0 |    0 |  0000-00-00 | 
2

これは古典的な組み合わせ爆発の問題であるとき、無関係なデータを含むあなたJOINテーブル。

各顧客の残高をサブクエリで計算する必要があります。そのサブクエリはcustomer_idごとに1行または0行を生成する必要があります。このように見えるかもしれません。 (http://sqlfiddle.com/#!9/560f2/8/0

 SELECT customer_id, 
      SUM(CASE WHEN type = 2 THEN -amount ELSE amount END) AS value 
     FROM table_wallet 
     GROUP BY customer_id 

は同様に、あなたは、サブクエリ(http://sqlfiddle.com/#!9/560f2/10/0)で各顧客の最新の順序を取得する必要があります。ここでも、customer_idごとに1行または0行のいずれかが必要です。

 SELECT customer_id, 
      MAX(order_id) AS order_id, 
      DATE(MAX(order_datetime)) AS order_date 
     FROM table_orders 
     GROUP BY customer_id 

その後、することができますLEFT JOIN彼らはあなたのtable_customersに、テーブルであるかのように、これら2つのサブクエリ。サブクエリテーブルです。彼らは仮想テーブルです。 (http://sqlfiddle.com/#!9/560f2/12/0

SELECT c.customer_id, 
     c.name, 
     w.value, 
     o.order_id, 
     o.order_date 
    FROM table_customers c 
    LEFT JOIN (
      SELECT customer_id, 
        SUM(CASE WHEN type = 2 THEN -amount ELSE amount END) AS value 
      FROM table_wallet 
      GROUP BY customer_id 
     ) w ON c.customer_id = w.customer_id 
    LEFT JOIN (
      SELECT customer_id, 
        MAX(order_id) AS order_id, 
        DATE(MAX(order_datetime)) AS order_date 
      FROM table_orders 
      GROUP BY customer_id 
     ) o ON c.customer_id = o.customer_id 

あなたの間違いは、このでした:あなたは、各顧客IDのための複数の行を持つ2つのテーブルそれぞれに参加しました。たとえば、特定の顧客が2つの注文と3つのWallet行を持っていた可能性があります。次に、結合により、ウォレットと注文行の可能な組み合わせをすべて表す6つの行が生成されます。それはコンビナトリアル爆発と呼ばれています。

私が概説した解決策では、各customer_idに参加する行が1つだけ(または行がない可能性がある)ことを確認して、組み合わせの爆発を排除します。

Pro tip:このようなサブクエリを使用すると、各サブクエリを個別にテストできます。

2

前の回答からさらに説明すると、単に文でグループを削除すると、なぜ二重カウントであるのかを簡単に確認できます。次のコード:

SELECT 
C.*, 
O.order_id, O.order_datetime, 
W.amount, W.type 
FROM 
table_customers as C 
LEFT JOIN 
table_wallet as W 
ON C.customer_id = W.customer_id 
LEFT JOIN 
table_orders AS O 
ON W.customer_id = O.customer_id 

は結果が得られます:

customer_id name order_id order_datetime   amount type 
1    abc 1   April, 22 2016 23:53:09 20  1 
1    abc 2   May, 22 2016 23:53:09 20  1 
1    abc 3   June, 22 2016 23:53:09 20  1 
2    def (null)  (null)     100  1 
2    def (null)  (null)     12.55 1 
3    pqrs (null)  (null)     (null) (null) 
4    wxyz (null)  (null)     (null) (null) 

注20

関連する問題