2016-03-10 4 views
1

Мне нужно назначить два значения для моего выбора на основе оператора CASE. В псевдо:Как назначить несколько значений в инструкции CASE?

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

Я задаю значение для snack. Но, допустим, я также хочу присвоить значение для другой переменной drink на основе тех же условий. Одним из способов было бы повторить выше:

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 
; 

Однако, если я должен назначить больше значения, основанные на тех же условиях, скажем food, drink, room и т.д. этот код становится трудно поддерживать.

Есть ли лучший способ сделать это? Могу ли я поместить это в функцию SQL, например, вы обычно делаете это на другом (скриптовом) языке, и если да, не могли бы вы объяснить, как это сделать?

+0

Использование функций повышения читабельности и ремонтопригодность, но они разрушат производительность. –

+1

create table Category (Categoryname, FoodName) использовать соединение с исходной таблицей –

+0

В этом случае я ценю ремонтопригодность и удобочитаемость по производительности, так как этот скрипт должен запускаться только один раз в день, в течение ночи. Я не ожидаю никаких проблем. – Pr0no

ответ

2

Функции разрушают производительность. Но вы могли бы использовать общий стол-выражение (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 
; 

На этом пути у вас есть только одно место для сохранения.

Если производительность запроса не имеет значения, и вы хотите использовать скаляр функция, как это:

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 

Затем вы можете использовать его в запросе таким образом:

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

Но более эффективным подходом было бы использование реальной таблицы для их хранения.

+0

И если мне нужна такая же информация в более позднем запросе, будет ли функция не лучше? – Pr0no

+1

@ Pr0no: тогда вам стоит подумать о том, чтобы сохранить его в реальной таблице. В большинстве случаев функция не подходит. Только если производительность запроса не имеет значения, это вариант. Это черная дыра для оптимизатора. –

+0

В этом случае производительность запроса не имеет значения. Не могли бы вы показать мне, как будет выглядеть такая функция? Это первый раз, когда я буду использовать функцию. Учитывая это и возможность обучения. – Pr0no

0

Надеется, что это поможет вам

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 
2

Когда делать вещи, как это, я предпочитаю использовать объединение с 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; 

Я считаю, что это наиболее гибким, если мне нужно добавить дополнительные условия или столбцы.

Или более многословным, но и более гибким:

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; 
Смежные вопросы