Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 995
Pr0no Avatar asked Mar 12 '23 23:03

Pr0no


2 Answers

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;
like image 69
GarethD Avatar answered Mar 15 '23 14:03

GarethD


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.

like image 43
Tim Schmelter Avatar answered Mar 15 '23 12:03

Tim Schmelter