views:

74

answers:

3

I have a customer table:

id   name
1    customer1
2    customer2
3    customer3

and a transaction table:

id   customer   amount   type
1    1          10       type1
2    1          15       type1
3    1          15       type2
4    2          60       type2
5    3          23       type1

What I want my query to return is the following table

name        type1    type2
customer1   2        1
customer2   0        1
customer3   1        0

Which shows that customer1 has made two transactions of type1 and 1 transaction of type2 and so forth.

Is there a query which I can use to obtain this result or do I have to use procedural code.

+1  A: 
SELECT  name, 
        (
        SELECT  COUNT(*)
        FROM    transaction t
        WHERE   t.customer = c.id
                AND t.type = 'type1'
        ) AS type1,
        (
        SELECT  COUNT(*)
        FROM    transaction t
        WHERE   t.customer = c.id
                AND t.type = 'type2'
        ) AS type2
FROM    customer c

To apply WHERE conditions to these columns use this:

SELECT  name
FROM    (
        SELECT  name, 
                (
                SELECT  COUNT(*)
                FROM    transaction t
                WHERE   t.customer = c.id
                        AND t.type = 'type1'
                ) AS type1,
                (
                SELECT  COUNT(*)
                FROM    transaction t
                WHERE   t.customer = c.id
                        AND t.type = 'type2'
                ) AS type2
        FROM    customer c
        ) q
WHERE   type1 > 3
Quassnoi
Yes this is a good solution but is there a shorter query if there are many types? Because I am working on many types in a huge dataset
andho
What you want is called pivoting. No, there is no simple way to do in in `MySQL`. Are you sure you need these type as columns, not rows?
Quassnoi
Yeah having them as rows will make everything much easier but don't necessarily need them as such
andho
It seems I am unable to use the new columns in a WHERE clause
andho
`@andho`: wrap the whole query into a subquery and select from it.
Quassnoi
yup that works!
andho
+2  A: 

You could try

select c.id as customer_id
   , c.name as customer_name
   , sum(case when t.`type` = 'type1' then 1 else 0 end) as count_of_type1
   , sum(case when t.`type` = 'type2' then 1 else 0 end) as count_of_type2
from customer c
   left join `transaction` t
   on c.id = t.customer
group by c.id, c.name

This query needs to iterate only once over the join.

Dirk
using `IF(t.type = 'type1', 1, 0)` is a bit more succinct, IMO, but +1 since this is the way to do it
nickf
oh actually, since "booleans" in MySQL are actually just 1 and 0, you don't even need the IF/CASE statement at all: `SUM(t.type = 'type1')` will do it.
nickf
Yeah. But even after a few years of C experience, I feel a little bit uncomfortable when I see code like `some_array[x == 1]`... :-) But I agree on the `IF` thingy being a little bit less verbose
Dirk
Wouldn't the 'case when' part only check one of the many columns in the transaction table for the customer
andho
@andho: Sorry, I do not understand your question. Is there more than a single column in the `transaction` table, where a customer ID may be found? If so, there is nothing in your post which would suggest so.
Dirk
@Dirk: no I just didn't understand the SQL but it works as expected. I thought the CASE WHEN clause will only check the first column of the GROUP
andho
+1  A: 

dirk beat me to it ;)

Similar but will work in mysql 4.1 too.

Select c.name,
sum(if(type == 1,1,0)) as `type_1_total`,
sum(if(type == 2,1,0)) as `type_2_total`,
from 
customer c
join transaction t on (c.id = t.customer)
;
txyoji