Question

I have to produce an ad-hoc 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 :)

Was it helpful?

Solution

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

OTHER TIPS

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

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

Personally, I think your 'longhand' way is elegant in that it is easy to read and maintain than I would find the answer @samjudson (but I do see the appeal of their approach). You could use OR to test more than one value per case. I find LIKE easier to read but that could just be me ;) e.g.

CASE
   WHEN card_type LIKE '37%' 
      THEN 'AMEX'
   WHEN (
         card_type LIKE '30%' 
         OR card_type LIKE '39%' 
        )  
      THEN 'Diners'
   WHEN (
         card_type LIKE '40%' 
         OR card_type LIKE '49%' 
        ) 
      THEN 'VISA'
   WHEN (
         card_type LIKE '50%' 
         OR card_type LIKE '59%' 
        ) 
      THEN 'MasterCard'  
   ELSE 
      'unknown' 
END
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top