Question

I need to select from two MySQL views, these are income and Expense views I need it such that if I do not have an expense for that record, it should come as 0 and likewise for Income.

This is the query I have so far:

select e.v_name,e.v_number,sum(e.cost) Expenses, sum(i.income)Income from expenses_view e,
income_view i where i.v_number = e.v_number
group by v_number;

I am only getting records which are in the two views using this query but I need to select even the records which do not have either income or expense from the view based on the Vehicle Number.

Was it helpful?

Solution

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.

  1. 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.
  2. The income and expense come from different tables.
  3. 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.

  1. v_number -> v_name
  2. v_number -> cost
  3. 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top