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 продовжує скаржитися на моє '=' у першому рядку КОЛИ ...

Як я можу це зробити?

Відповіді:


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)

2
Це дасть дещо інший результат, оскільки заява Case закінчується після виконання умови - але синтаксис АБО оцінить усі можливості
тембер

1
@tember Навіть якщо SQL був процедурною мовою, якщо перший АБО істинний, то решта вираження не оцінюється. Оскільки SQL є декларативною мовою, то як ви знаєте, що DBM оцінить усі АБО? Щире запитання, я не розумію.
ArturoTena

У SQL решта вираження оцінюється в синтаксисі АБО. Спробуйте це (це не дозволить мені включити символ @ - вам доведеться виправити його, якщо ви хочете перевірити його): оголосити var varchar (5) встановити 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

5
Ну, я б написав, що як SELECT column1, column2 FROM viewWWWERE WHERE (@locationType = 'location' AND account_location = @locationID) АБО (@locationType = 'область' І xxx_location_area = @locationID) АБО (@locationType = 'поділ') І xxx_location_division = @locationID)
Jan de Vos

2
це чудовий приклад світу, як щодо плану виконання запиту з найкращим відповіддю (особисто я вважаю за краще, щоб цей метод був зрозумілим і чистим)
PEO

1
дійсно чудово, якщо ви хочете використовувати інше, де застереження з різним типом, наприклад, int на першому і nvarchar на 2. з рішенням Bob Probst це не працює. дякую
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
    )

І так далі ... Ми не можемо змінити структуру запиту під час руху, але ми можемо їх замінити, зробивши присудки рівними.

EDIT: Наведені вище пропозиції, звичайно, набагато краще, просто ігноруйте мої.


Я не думаю, що це хибна структура таблиці. Таблиця була створена таким чином, щоб мати самостійні посилання мати нескінченну кількість відносин батько / дитина. Повірте, це було цілеспрямовано. Я не думаю, що я хочу змінити структуру таблиці, щоб просто використовувати оператор переключення. це не так важливо
Майлз

5

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

Ви можете використовувати пункт WHERE під час перевірки критеріїв WHERE у предикаті, наприклад

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

тому у вашому конкретному випадку вам потрібно буде поставити запит у збережену процедуру або створити три окремі запити.


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
Для того, щоб той , хто читає це в майбутньому: це те ж саме , як загальноприйнятому відповідь від @ боб-Проста вище: 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
    )
)

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
Для того, щоб той , хто читає цей відповідь: це те ж саме , як загальноприйнятому відповідь від @ боб-Проста вище: stackoverflow.com/a/206500/264786 Downvoted з цієї причини.
ArturoTena
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.