SQL Switch / Case в предложении 'where'

146

Я пытался искать вокруг, но я не мог найти ничего, что помогло бы мне.

Я пытаюсь сделать это в SQL:

declare @locationType varchar(50);
declare @locationID int;

SELECT column1, column2
FROM viewWhatever
WHERE
CASE @locationType
    WHEN 'location' THEN account_location = @locationID
    WHEN 'area' THEN xxx_location_area = @locationID
    WHEN 'division' THEN xxx_location_division = @locationID

Я знаю, что мне не нужно было ставить '= @locationID' в конце каждого, но я не могу получить синтаксис, даже близкий к правильному. SQL продолжает жаловаться на мой '=' в первой строке WHEN ...

Как я могу это сделать?

миль
источник

Ответы:

191
declare @locationType varchar(50);
declare @locationID int;

SELECT column1, column2
FROM viewWhatever
WHERE
@locationID = 
  CASE @locationType
      WHEN 'location' THEN account_location
      WHEN 'area' THEN xxx_location_area 
      WHEN 'division' THEN xxx_location_division 
  END
Боб Пробст
источник
1
Как отметил TomH в комментарии к вашему ответу ниже, вы неправильно сформировали SQL. Я проверил мой в SQLServer 2005, и он работал нормально.
Боб Пробст
Зачем нужны @? Что они делают?
Тадей
2
@ указывает переменные в t-sql, без @, @locationID будет интерпретироваться как имя столбца.
Кристиан Слоупер
70

без постановки дела ...

SELECT column1, column2
FROM viewWhatever
WHERE
    (@locationType = 'location' AND account_location = @locationID)
    OR
    (@locationType = 'area' AND xxx_location_area = @locationID)
    OR
    (@locationType = 'division' AND xxx_location_division = @locationID)
Lukek
источник
2
Это даст немного другой результат, так как оператор Case завершает работу после выполнения условия - но синтаксис OR оценит все возможности
тимбер
1
@tember Даже если SQL был процедурным языком, если первое ИЛИ истинно, то остальная часть выражения не оценивается. Поскольку SQL является декларативным языком, как вы узнаете, что DBM будет оценивать все OR? Искренний вопрос, я не понимаю.
ArturoTena
В SQL остальная часть выражения вычисляется в синтаксисе OR. Попробуйте это (это не позволит мне включить символ @ - вам придется исправить его, если вы хотите проверить это): объявляйте var varchar (5) set var = '0' выберите 2 / var где var <> 0 или ISNUMERIC (var) = 1. Я хочу, чтобы условие вышло из-за того, что var IS равен 0, но он проверяет, является ли оно числовым, и поэтому инструкция возвращает ошибку.
тябрь
это помогло в моем случае, когда у меня был другой оператор сравнения для каждого значения типа.
Алекс
еще лучше DECLARE @locationType NVARCHAR(50) = 'youchoose' IF @locationType = 'location' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (account_location = @locationID) END IF @locationType = 'area' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (xxx_location_area = @locationID) END IF @locationType = 'division' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (xxx_location_division = @locationID) END
Лукек
35

Ну вот.

SELECT
   column1, 
   column2
FROM
   viewWhatever
WHERE
CASE 
    WHEN @locationType = 'location' AND account_location = @locationID THEN 1
    WHEN @locationType = 'area' AND xxx_location_area = @locationID THEN 1
    WHEN @locationType = 'division' AND xxx_location_division = @locationID THEN 1
    ELSE 0
END = 1
Питтсбург DBA
источник
5
Ну, я бы написал это как SELECT column1, column2 FROM viewWh независимо от того, ГДЕ (@locationType = 'location' AND account_location = @locationID) ИЛИ (@locationType = 'area' И xxx_location_area = @locationID) ИЛИ (@locationType = 'Division' AND xxx_location_division = @locationID)
Ян де Вос
2
это прекрасный пример, как насчет плана выполнения запроса с лучшим ответом (лично я предпочитаю, чтобы этот код метода был ясным и чистым)
PEO
1
действительно здорово, если вы хотите использовать разные операторы where с разными типами, например, int в первом предложении и nvarchar во втором. с Бобом Пробстом решение не работает. спасибо
Julian50
6

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

Если вы не можете изменить структуру, может сработать что-то вроде приведенного ниже:

SELECT
    *
FROM
    Test
WHERE
    Account_Location = (
        CASE LocationType
          WHEN 'location' THEN @locationID
          ELSE Account_Location
        END
    )
    AND
    Account_Location_Area = (
        CASE LocationType
          WHEN 'area' THEN @locationID
          ELSE Account_Location_Area
        END
    )

И так далее ... Мы не можем изменить структуру запроса на лету, но мы можем переопределить его, сделав предикаты равными себе.

РЕДАКТИРОВАТЬ: вышеупомянутые предложения, конечно, намного лучше, просто игнорируйте мои.

Марк С. Расмуссен
источник
Я не думаю, что это некорректная структура таблицы. Таблица была составлена ​​таким образом, чтобы это были собственные ссылки, чтобы иметь бесконечное количество родительских / дочерних отношений. Поверьте мне, это было специально. Я не думаю, что хочу изменить структуру таблицы, чтобы просто использовать оператор switch. это очень важно
Майлз
5

Проблема заключается в том, что когда движок SQL анализирует выражение, он проверяет часть FROM, чтобы получить правильные таблицы, а затем часть WHERE, чтобы предоставить некоторые базовые критерии, поэтому он не может правильно оценить динамическое условие, для какого столбца проверить против.

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

WHERE account_location = CASE @locationType
                              WHEN 'business' THEN 45
                              WHEN 'area' THEN 52
                         END

поэтому в вашем конкретном случае вам нужно поместить запрос в хранимую процедуру или создать три отдельных запроса.

Dillie-О
источник
5

Оператор ИЛИ может быть альтернативой случая, когда в условии где

ALTER PROCEDURE [dbo].[RPT_340bClinicDrugInventorySummary]
    -- Add the parameters for the stored procedure here
     @ClinicId BIGINT = 0,
     @selecttype int,
     @selectedValue varchar (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
    drugstock_drugname.n_cur_bal,drugname.cdrugname,clinic.cclinicname

FROM drugstock_drugname
INNER JOIN drugname ON drugstock_drugname.drugnameid_FK = drugname.drugnameid_PK
INNER JOIN drugstock_drugndc ON drugname.drugnameid_PK = drugstock_drugndc.drugnameid_FK
INNER JOIN drugndc ON drugstock_drugndc.drugndcid_FK = drugndc.drugid_PK
LEFT JOIN clinic ON drugstock_drugname.clinicid_FK = clinic.clinicid_PK

WHERE   (@ClinicId = 0 AND 1 = 1)
    OR  (@ClinicId != 0 AND drugstock_drugname.clinicid_FK = @ClinicId)

    -- Alternative Case When You can use OR
    AND ((@selecttype = 1 AND 1 = 1)
    OR  (@selecttype = 2 AND drugname.drugnameid_PK = @selectedValue)
    OR  (@selecttype = 3 AND drugndc.drugid_PK = @selectedValue)
    OR  (@selecttype = 4 AND drugname.cdrugclass = 'C2')
    OR  (@selecttype = 5 AND LEFT(drugname.cdrugclass, 1) = 'C'))

ORDER BY clinic.cclinicname, drugname.cdrugname
END
Атик Саркер
источник
3

Пожалуйста, попробуйте этот запрос. Ответ на пост выше:

select @msgID, account_id
    from viewMailAccountsHeirachy
    where 
    CASE @smartLocationType
        WHEN 'store' THEN account_location
        WHEN 'area' THEN xxx_location_area 
        WHEN 'division' THEN xxx_location_division 
        WHEN 'company' THEN xxx_location_company 
    END  = @smartLocation
Дурре Наджаф
источник
2
Для тех, кто читает это в будущем: это так же, как принятый ответ от @ bob-prost выше: stackoverflow.com/a/206500/264786
ArturoTena
2

Попробуй это:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
shah134pk
источник
1
Проголосовал, потому что я не понимаю, как это можно выбрать между заданными строками (то есть «местоположение», «площадь», «деление»)
ArturoTena
0
CREATE PROCEDURE [dbo].[Temp_Proc_Select_City]
    @StateId INT
AS  
        BEGIN       
            SELECT * FROM tbl_City 
                WHERE 
                @StateID = CASE WHEN ISNULL(@StateId,0) = 0 THEN 0 ELSE StateId END ORDER BY CityName
        END
Даршан Балар
источник
-1
Case Statement in SQL Server Example

Syntax

CASE [ expression ]

   WHEN condition_1 THEN result_1
   WHEN condition_2 THEN result_2
   ...
   WHEN condition_n THEN result_n

   ELSE result

END

Example

SELECT contact_id,
CASE website_id
  WHEN 1 THEN 'TechOnTheNet.com'
  WHEN 2 THEN 'CheckYourMath.com'
  ELSE 'BigActivities.com'
END
FROM contacts;

OR

SELECT contact_id,
CASE
  WHEN website_id = 1 THEN 'TechOnTheNet.com'
  WHEN website_id = 2 THEN 'CheckYourMath.com'
  ELSE 'BigActivities.com'
END
FROM contacts;
Кавита Редди
источник
4
Проголосовал, потому что этот ответ не связан с вопросом. Вопрос заключался в том, как использовать CASE в предложении WHERE, а не в том, как использовать CASE в столбце SELECTed.
ArturoTena
-2

Попробуйте этот запрос. Это очень легко понять:

CREATE TABLE PersonsDetail(FirstName nvarchar(20), LastName nvarchar(20), GenderID int);
GO

INSERT INTO PersonsDetail VALUES(N'Gourav', N'Bhatia', 2),
              (N'Ramesh', N'Kumar', 1),
              (N'Ram', N'Lal', 2),
              (N'Sunil', N'Kumar', 3),
              (N'Sunny', N'Sehgal', 1),
              (N'Malkeet', N'Shaoul', 3),
              (N'Jassy', N'Sohal', 2);
GO

SELECT FirstName, LastName, Gender =
    CASE GenderID
    WHEN 1 THEN 'Male'
    WHEN 2 THEN 'Female'
    ELSE 'Unknown'
    END
FROM PersonsDetail
Майк Кларк
источник
1
Для того, кто читает этот ответ: он такой же, как принятый ответ от @ bob-prost выше: stackoverflow.com/a/206500/264786 По этой причине проголосовал за него.
ArturoTena