Использование case вместо IF ELSE для изменения предложения WHERE в SQL

Я пытаюсь построить запрос, в котором предложение where немного меняется в зависимости от внешней переменной, и я придумал скрипт ниже

IF(@threshold='N')

  select  min_price
  ,       s.customization_type
  ,       gii.brand
  ,       gii.item_flag
  from   item_info gii
  ,      style s
  ,      categories gpc  
  where  isnull(@ordered,getdate()) between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
  and    gii.segment1 = s.style_number   
  and    gpc.line_of_business_category = 'AAA'
  and    gii.inventory_item_id = @v_item_id;

ELSE 
      select  min_price
  ,       s.customization_type
  ,       gii.brand
  ,       gii.item_flag
  from   item_info gii
  ,      style s
  ,      categories gpc  
  where  isnull(@ordered,getdate()) between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
  and    gii.segment1 = s.style_number   
  and    gpc.line_of_business_category = 'BBB'
  and    gii.inventory_item_id = @v_item_id;

Но я чувствую, что приведенный выше запрос не является чистым, хотя и дает желаемые результаты. Есть ли лучший способ оптимизировать этот запрос, например, используя CASE WHEN?

6 ответов

Насколько я вижу, вам нужно только изменить эту строку:

gpc.line_of_business_category = 'AAA'

в

gpc.line_of_business_category = CASE 
                                   WHEN @threshold='N' THEN 'AAA' 
                                   ELSE 'BBB'
                                END

Если вы используете SQL Server 2012 или новее, то IIF другая альтернатива:

gpc.line_of_business_category = IIF(@threshold='N', 'AAA', 'BBB') 

Вы можете использовать как ниже:

where gpc.line_of_business = case when @threshold='N' then 'AAA' else 'BBB' end
  select  min_price
  ,       s.customization_type
  ,       gii.brand
  ,       gii.item_flag
  from   item_info gii
  ,      style s
  ,      categories gpc  
  where  isnull(@ordered,getdate()) between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
  and    gii.segment1 = s.style_number   
  and    gii.inventory_item_id = @v_item_id;

  and    gpc.line_of_business_category = (CASE WHEN @threshold='N' THEN 'AAA' ELSE 'BBB' END)

Но вы должны использовать явный синтаксис соединения!

При использовании синтаксиса явного соединения это делает ваши намерения более понятными для других при отладке кода:

select
    min_price
    ,s.customization_type
    ,gii.brand
    ,gii.item_flag
from 
    item_info gii
    INNER JOIN style s
    ON gii.segment1 = s.style_number
    INNER JOIN categories gpc
    ON isnull(@ordered,getdate()) between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
    and gpc.line_of_business_category = (CASE WHEN @threshold='N' THEN 'AAA' ELSE 'BBB' END)
WHERE
    gii.inventory_item_id = @v_item_id;

Кроме того, он начинает показывать вам, когда у вас могут возникнуть проблемы в отношениях, например, в случае ваших категорий gpc. Я вижу, что таблица не имеет прямого отношения к 1 из других таблиц, а вы выбираете категории, которые затем перекрестно соединяются, что вы хотели?

Конечно, вы можете использовать case выражение:

select  min_price
  ,       s.customization_type
  ,       gii.brand
  ,       gii.item_flag
  from   item_info gii
  ,      style s
  ,      categories gpc  
  where  isnull(@ordered,getdate()) between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
  and    gii.segment1 = s.style_number   
  and    gpc.line_of_business_category = case when @threshold='N' then 'AAA' else 'BBB' end
  and    gii.inventory_item_id = @v_item_id;

Вы действительно можете использовать оператор CASE в своем запросе, как показано ниже, с минимальным изменением запроса:

select min_price
    , s.customization_type
    , gii.brand
    , gii.item_flag
from   item_info gii
    , style s
    , categories gpc  
where isnull(@ordered,getdate()) 
    between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
    and gii.segment1 = s.style_number   
    and 1 = CASE 
             WHEN @threshold='N' AND gpc.line_of_business_category = 'AAA'
                 THEN 1
             WHEN @threshold <> 'N' AND gpc.line_of_business_category = 'BBB'
                 THEN 1
          ELSE 0
      END
and gii.inventory_item_id = @v_item_id;

Просто используйте

  and ( (@threshold ='N' AND gpc.line_of_business_category = 'AAA')
     or (@threshold<>'N' AND gpc.line_of_business_category = 'BBB') )

Полный запрос будет

select  min_price
  ,       s.customization_type
  ,       gii.brand
  ,       gii.item_flag
  from   item_info gii
  ,      style s
  ,      categories gpc  
  where  isnull(@ordered,getdate()) between gpc.start_date and isnull(gpc.end_date, dateadd(day, 1, getdate()))
  and    gii.segment1 = s.style_number   
  and ( (@threshold ='N' AND gpc.line_of_business_category = 'AAA')
     or (@threshold<>'N' AND gpc.line_of_business_category = 'BBB') )
  and    gii.inventory_item_id = @v_item_id;
Другие вопросы по тегам