views:

93

answers:

2

I have 2 tables in my database: item and category. Items can be active, or inactive, and have a categoryID that relates to the id of a record in the category table.

i want to perform a query to show all the categories, with the total cost of active items for the category

So my goal is to return something looking like this:

    +--------+------------+---------------+
    | id     | cat_name   | total_cost    |
    +--------+------------+---------------+
    |      1 | cat 1      | 12            |
    |      2 | cat 2      | 0             |
    |      3 | cat 3      | 45            |
    +--------+------------+---------------+

My first query:

    SELECT a.*, 
    SUM(b.cost) AS total_cost
    FROM categories a LEFT JOIN items b
    ON(a.id = b.category_id)
    GROUP BY a.category_name

works ok, but it returns NULL items instead of 0, and uses all items regardless of active/inactive:

    +--------+------------+---------------+
    | id     | cat_name   | total_cost    |
    +--------+------------+---------------+
    |      1 | cat 1      | 44            |
    |      2 | cat 2      | NULL          |
    |      3 | cat 3      | 87            |
    +--------+------------+---------------+

my second query adresses the NULL values:

    SELECT a.*, 
    SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
    FROM categories a LEFT JOIN items b
    ON(a.id = b.category_id)
    GROUP BY a.category_name

and turns out like so:

    +--------+------------+---------------+
    | id     | cat_name   | total_cost    |
    +--------+------------+---------------+
    |      1 | cat 1      | 44            |
    |      2 | cat 2      | NULL          |
    |      3 | cat 3      | 87            |
    +--------+------------+---------------+

So in my tiny useless brain i try the following query, adding a WHERE clause on table b where active has to = 1 (true)

    SELECT a.*, 
    SUM(IF(b.cost IS NULL, 0, b.cost)) AS total_cost
    FROM categories a LEFT JOIN items b
    ON(a.id = b.category_id)
    WHERE b.active = 1 
    GROUP BY a.category_name

and i get the following:

    +--------+------------+---------------+
    | id     | cat_name   | total_cost    |
    +--------+------------+---------------+
    |      1 | cat 1      | 12            |
    |      3 | cat 3      | 45            |
    +--------+------------+---------------+

so as you can se, i would like to return the entire range of categories, even when the right table returns no matching results... Any takes for a million imaginary cool points?

+1  A: 

Try this:

  SELECT a.*,  
    SUM(Case B.Active When 1 Then b.cost else 0 End) AS total_cost 
    FROM categories a 
       LEFT JOIN items b 
         ON b.category_id = a.id  
    GROUP BY a.category_name 

or this:

    SELECT a.*, SUM(b.cost) AS total_cost 
    FROM categories a 
       LEFT JOIN items b 
         ON b.category_id = a.id 
            And B.Active = 1 
    GROUP BY a.category_name 
Charles Bretana
You are also totally bodacious and earn a gazillion imaginary cool points - thank you. Both of these make a lot of sense, and of course work. Do you have any wisdom on why one method would be 'better' use than another? from a readability perspective i find it easier to understand the first query even though it is more verbose.
Beans
Also, the first one returns null values instead of zero for the total_cost but thats an easy fix. And i think the second one should have b.active = 1, instead of b.cost = 1
Beans
ahh yes, typo... b.active it should be!
Charles Bretana
+3  A: 

Use:

   SELECT c.id,
          c.cat_name,
          COALESCE(SUM(i.cost), 0) AS total_cost
     FROM CATEGORIES c
LEFT JOIN ITEMS i ON i.category_id = c.category_id
                 AND i.active = 1
 GROUP BY c.id, c.cat_name
OMG Ponies
it works, no prob. but in mysql you dont need to group by `cat_name`. `id` is suffice. If you want it to be more effective and compliant with SQL standard then use `max(c.cat_name)` or similar group by function in column expressions.
Imre L
@Imre L: Using MAX would hide if there were two or more cat_name values associated to the same id, assuming possible. There's nothing non-standard about defining columns in the group by that don't have aggregate functions on them, and assuming the id value is unique then it's personal choice (though MAX/etc implies that there could be duplicates).
OMG Ponies
Bravo good sir - award yourself a big pat on the bag an as many imaginary cool points as you can carry.I need to do some reading into this coalesce statement to full understand whats going on.
Beans
@Beans: The [COALESCE](http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_coalesce) function is an ANSI SQL function that is designed to return the next value (starting from the left) if the current value is NULL. It's like using [IFNULL](http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html#function_ifnull), but portable to other databases and supports more than two parameters.
OMG Ponies