Pr0no
Pr0no

Reputation: 4099

How to assign multiple values in CASE statement?

I need to assign two values to my select based on a CASE statement. In pseudo:

select
    userid
  , case
        when name in ('A', 'B') then 'Apple'
        when name in ('C', 'D') then 'Pear'
    end as snack
from
    table
;

I am assigning a value for snack. But lets say I also want to assign a value for another variable, drink based on the same conditions. One way would be to repeat the above:

select
    userid
  , case
        when name in ('A', 'B') then 'Apple'
        when name in ('C', 'D') then 'Pear'
    end as snack
  , case
        when name in ('A', 'B') then 'Milk'
        when name in ('C', 'D') then 'Cola'
    end as drink
from
    table
;

However, if I have to assign more values based on the same conditions, say food, drink, room, etc. this code becomes hard to maintain.

Is there a better way of doing this? Can I put this in a SQL function, like you would normally do in another (scripting) language and if so, could you please explain how?

Upvotes: 5

Views: 31662

Answers (4)

alexherm
alexherm

Reputation: 1362

As Ganesh suggested in a comment, I recommend creating a mapping table for this and just do lookups. Much easier to interpet, maintain, and scale - all with better performance.

Upvotes: 0

Abdul Rasheed
Abdul Rasheed

Reputation: 6729

Hope this will help you

SELECT userid
        ,(CASE flag WHEN 1 THEN 'Apple' WHEN 2 THEN 'Pear' WHEN 3 THEN '..etc' END ) as snack
        ,(CASE flag WHEN 1 THEN 'Milk'  WHEN 2 THEN 'Cola' WHEN 3 THEN '..etc' END ) as drink
FROM    (
    SELECT userid
            ,(  CASE    WHEN name IN ('A', 'B') THEN 1 
                    WHEN name IN ('C', 'D') THEN 2 
                    WHEN name IN ('X', 'Y') THEN 3
                ELSE 0 END )    AS flag
    FROM table ) t

Upvotes: 1

Tim Schmelter
Tim Schmelter

Reputation: 460208

Functions destroy performance. But you could use a common-table-expression(cte):

with cte as
(
    Select IsNameInList1 = case when name in ('A', 'B') 
                           then 1 else 0 end,
           IsNameInList2 = case when name in ('C', 'D') 
                           then 1 else 0 end,
           t.*
    from table
)
select
    userid
  , case when IsNameInList1=1 then 'Apple'
         when IsNameInList2=1 then 'Pear'
    end as snack
  , case when IsNameInList1=1 then 'Milk'
         when IsNameInList2=1 then 'Cola'
    end as drink
from
    cte
;

On this way you have only one place to maintain.

If query performance doesn't matter and you want to use a scalar valued function like this:

CREATE FUNCTION [dbo].[IsNameInList1] 
(
    @name varchar(100)
)
RETURNS bit
AS
BEGIN
DECLARE @isNameInList bit

BEGIN
    SET @isNameInList =  
    CASE WHEN @name in ('A', 'B')
    THEN 1
    ELSE 0 
    END
END

 RETURN @isNameInList 
END

Then you can use it in your query in this way:

select
    userid
  , case when dbo.IsNameInList1(name) = 1 then 'Apple'
         when dbo.IsNameInList2(name) = 1 then 'Pear'
    end as snack
from
    table
;

But a more efficient approach would be to use a real table to store them.

Upvotes: 2

GarethD
GarethD

Reputation: 69789

When doing things like this I tend to use a join with a table valued constructor:

SELECT  t.UserID,
        s.Snack,
        s.Drink
FROM    Table AS T
        LEFT JOIN
        (VALUES
            (1, 'Apple', 'Milk'),
            (2, 'Pear', 'Cola')
        ) AS s (Condition, Snack, Drink)
            ON s.Condition = CASE 
                                WHEN t.name IN ('A', 'B') THEN 1
                                WHEN t.name IN ('C', 'D') THEN 2
                            END;

I find this to be the most flexible if I need to add further conditions, or columns.

Or more verbose, but also more flexible:

SELECT  t.UserID,
        s.Snack,
        s.Drink
FROM    Table AS T
        LEFT JOIN
        (VALUES
            ('A', 'Apple', 'Milk'),
            ('B', 'Apple', 'Milk'),
            ('C', 'Pear', 'Cola'),
            ('D', 'Pear', 'Cola')
        ) AS s (Name, Snack, Drink)
            ON s.Name= t.name;

Upvotes: 5

Related Questions