tags:

views:

646

answers:

3

I have to produce an ad hock report on the number of transactions made with different credit card types. For the purposes of the report it is fine to assume that all credit cards that start with a 4 are VISA cards and that those that start with a 5 are MasterCard.

This query works well for the above distinctions:

select card_type = 
    case substring(pan,1,1) 
        when '4' then 'VISA'
        when '5' then 'MasterCard'
        else 'unknown' 
    end,count(*),
    sum(amount)
from transactions
group by card_type

However in our situation (not sure how this works world wide) all cards that start with a 3 can be considered Diners Club Cards except for those that start with a 37 which are AMEX cards.

Extending the above query like this seems like a complete hack

select card_type = 
    case substring(pan,1,2) 
        when '30' then 'Diners'
        ...
        when '37' then 'AMEX'
        ...
        when '39' then 'Diners'
        when '40' then 'VISA'
        ...
        when '49' then 'VISA'
        when '50' then 'MasterCard'  
        ...
        when '59' then 'MasterCard'  
        else 'unknown' 
    end,count(*),
    sum(amount)
from transactions
group by card_type

Is there an elegant way of grouping by the first digit in all cases except where the first two digits match the special case?

I also have no idea how to Title this question if anyone wants to help out...

EDIT: I had the values for MasterCard and VISA mixed up, so just to be correct :)

+3  A: 

You can do case statements like the following:

select case
    when substring(pan,1,2) = '37' then 'AMEX'
    when substring(pan,1,1) = '3' then 'Diners'
    when substring(pan,1,1) = '4' then 'Mastercard'
    when substring(pan,1,1) = '5' then 'VISA'
    else 'unknown' 
end,
count(*),
sum(amount)
from transactions
group by card_type
samjudson
+1 perfect answer. Should also add that the ordering of the case statements is important; check for '37' before checking for '3', otherwise the '3' will fire first
Kev Riley
+1  A: 

you could just store the card type column in your table and FK to a card type table, or try something like:

CASE
    WHEN LEFT(pan,2)='37' then ...
    WHEN LEFT(pan,1)='3' then ...
    .....

EDIT
you should really consider storing a card type value in a table. Determine it one time when inserting and then then you can query your data without jumping through these hoops each time. You will also protect yourself if the algorithm changes at some point, all existing data will be correct

KM
It would obviously be a better solution to store the card type in the table. But like most of us I am sure you have had to work with legacy systems that everyone else is to scared to modify. If this report was going to be run more often I would fight harder for the better solution.
Ron Tuffin
+2  A: 

Not sure about your system, but in Oracle CASE expressions are exactly that, so you can nest them:

case substring(pan,1,1)
        when '3' then case substring(pan,2,1)
                             when '7' then 'Amex'
                             else 'Diners'
                      end
        when '4' then 'VISA'
        when '5' then 'MasterCard'  
        else 'unknown'
end
Dave Costa
Apart from a missing 'end' keyword from T-SQL the above is what I was going to suggest.
MattH
Whoops, added the "end" -- would be needed in Oracle too
Dave Costa