Let's begin by clarifying the specification for your result set. I'm spelling this out because I strongly believe that good SQL flows naturally from a clear specification.
- You want one row per (v_name,v_number) that will show the aggregate of both income and expense, with zeros showing where there are none of either of these.
- The income and expense come from different tables.
- You don't have (at least you haven't disclosed in your question) a separate table that contains a definitive list of (v_name,v_number) values.
So, we first need to generate a definitive list of your v_numbers. That's easy
SELECT DISTINCT v_name, v_number FROM (
SELECT v_name, v_number FROM expenses_view
UNION
SELECT v_name, v_number FROM income_view
) AS u
Now look, this is a truly horrible way to do this. What we need is some table with all the person records in it. But, no matter. Sometimes enterprise software needs this kind of stuff because of data hiding.
Next we need to summarize the income and expenses. That will work like this:
SELECT v_number, SUM(cost) AS cost FROM expenses_view GROUP BY v_number
and
SELECT v_number, SUM(income) AS income FROM expenses_view GROUP BY v_number
You have to do the aggregating with SUM()
before you join these tables, because JOIN operations generate combinatorial explosion. If you do the SUM()
operations after the JOINs your sums will be way too large; you'll use each detail record too many times.
So, here we have three queries, each generating a virtual table, each of which has one row per v_number.
- v_number -> v_name
- v_number -> cost
- v_number -> income
If you're smart you'll unit-test each one of these independently.
Now, we join them together, with LEFT JOINs, to get your desired result set. We need LEFT JOIN because ordinary JOIN will omit rows that don't have both income and expense records.
SELECT v.v_name, v.v_number,
IFNULL(e.cost,0) AS cost,
IFNULL(i.income,0) AS income
FROM (
/* first virtual table */
) AS v
LEFT JOIN (
/* second virtual table */
) AS e ON v.v_number = e.v_number
LEFT JOIN (
/* third virtual table */
) AS i ON v.v_number = i.v_number
See how that goes? We generate the lists of what we need (people, costs, incomes) and then we join those lists together to get the final result.
Here's the actual query.
SELECT v.v_name, v.v_number,
IFNULL(e.cost,0) AS cost,
IFNULL(i.income,0) AS income
FROM (
/* first virtual table */
SELECT DISTINCT v_name, v_number FROM (
SELECT v_name, v_number FROM expenses_view
UNION
SELECT v_name, v_number FROM income_view
) AS u
) AS v
LEFT JOIN (
/* second virtual table */
SELECT v_number, SUM(cost) AS cost FROM expenses_view GROUP BY v_number
) AS e ON v.v_number = e.v_number
LEFT JOIN (
/* third virtual table */
SELECT v_number, SUM(income) AS income FROM expenses_view GROUP BY v_number
) AS i ON v.v_number = i.v_number
This may be a bit more than you bargained for. But it's the structured and predictable way to get the result set you need.