Reputation: 83
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
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
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
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