skeith
skeith

Reputation: 83

combining mysql query SUM() result using inner join

I have these two tables

table1 : date | uid | value_in
table2 : date | uid | value_out 

each day, every uid will receive a balance value for both in and out, regardless of the number of transaction/records

what I want to do is to combine the query result to be like this

date | uid | value_in | value_out | (value_in-value_out) as balance

however, when I do this query

SELECT 
  a.date,
  a.uid,
  SUM(a.value_in),
  SUM(b.value_out),
  (SUM(a.value_in)-SUM(b.value_out)) AS balance
FROM table1 a
INNER JOIN table2 b ON a.date=b.date AND a.uid=b.uid
GROUP BY a.date, a.uid

it produce invalid result (the SUM is doubled or tripled) how should I modify my query so it does not produce doubled result ?

Upvotes: 2

Views: 4144

Answers (3)

goodmorning
goodmorning

Reputation: 1

SELECT 
  a.date,
  a.uid,
  SUM(a.value_in),
  SUM(b.value_out),
  SUM(a.value_in-b.value_out) AS balance 
FROM table1 a
INNER JOIN table2 b ON a.date=b.date AND a.uid=b.uid
GROUP BY a.date, a.uid`enter code here`

Upvotes: 0

Erwin Brandstetter
Erwin Brandstetter

Reputation: 656331

First build the sum, then join. Like this:

SELECT i.uid
      ,i.date
      ,i.v_in
      ,COALESCE(o.v_out, 0) AS v_out
      ,(i.v_in - COALESCE(o.v_out, 0)) AS balance
FROM (
    SELECT date
          ,uid
          ,SUM(value_in) AS v_in
    FROM   table1
    GROUP  BY 1,2
    ) i
LEFT JOIN (
    SELECT date
          ,uid
          ,SUM(value_out) AS v_out
    FROM   table2
    GROUP  BY 1,2
    ) o USING (date, uid)

The way you had it, every value_in would be combined with every matching value_out, thereby multiplying the numbers. You must aggregate first and then you join one in-sum with one out-sum and everything is groovy. Or is it?

What happens if there are no value_in or value_out for a given (date, uid)? Or only value_in Or just value_out? Your query will fail.

I improved by using a LEFT JOIN instead of [INNER] JOIN, but what you really want is a FULL OUTER JOIN - one of the missing features in MySQL.

You can either provide a list of days in a separate table and LEFT JOIN both tables to it, or you can work around the missing feature with two times LEFT JOIN and UNION. See here for an example.

Or you could multiply your value_out by -1 UNION both tables together and build one sum.

But you still would not get a row for a day without any value_in or value_out, which violates your description.

So, the only clean solution is to have a table with all (date, uid) you want in the result and LEFT JOIN the sums of table1 and table2 to it, or UNION ALL the three (negative table2) in a sub-select and then sum up.

Upvotes: 3

Milan Jaric
Milan Jaric

Reputation: 5646

What will be result if you try this

SELECT 
  a.date,
  a.uid,
  SUM(a.value_in),
  SUM(b.value_out),
  SUM(a.value_in-b.value_out) AS balance 
FROM table1 a
INNER JOIN table2 b ON a.date=b.date AND a.uid=b.uid
GROUP BY a.date, a.uid

SUM(a.value_in-b.value_out) AS balance should be logically same as (SUM(a.value_in)-SUM(b.value_out)) AS balance but maybe not to mysql

Upvotes: 0

Related Questions