179

I tried searching around, but I couldn't find anything that would help me out.

I'm trying to do this in 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

I know that I shouldn't have to put '= @locationID' at the end of each one, but I can't get the syntax even close to being correct. SQL keeps complaining about my '=' on the first WHEN line...

How can I do this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Miles
  • 5,646
  • 18
  • 62
  • 86

14 Answers14

221
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
Bob Probst
  • 9,533
  • 8
  • 32
  • 41
82

without a case statement...

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)
Sk8erPeter
  • 6,899
  • 9
  • 48
  • 67
Lukek
  • 1,036
  • 8
  • 6
  • 7
    This is will give a slightly different result as the Case statement exits after a condition is met - but the OR syntax will evaluate all the possibilities – tember May 12 '15 at 18:27
  • 1
    @tember Even if SQL were a procedural language, if the first OR is true then the rest of the expression doesn't get evaluated. Since SQL is a declarative language, how do you know the DBM will evaluated all the ORs? Sincere question, I don't understand. – ArturoTena Jun 19 '15 at 20:25
  • 2
    In SQL the rest of the expression does get evaluated in the OR syntax. Try this (it wouldn't let me include the @ symbol - you will have to correct it if you want to test it): declare var varchar(5) set var = '0' select 2 / var where var <> 0 or ISNUMERIC(var) = 1 . I want the condition to exit because var IS equal to 0, but it goes ahead to check if it is numeric, which it is, and therefore the statement returns an error. – tember Jun 22 '15 at 16:43
  • this one helped in my case where I had a different compare operator for each value of the type. – Alex Oct 21 '16 at 16:43
  • better still `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` – Lukek Jan 27 '17 at 14:47
  • The version I have swaps the AND with the OR. While this technically does work, I have found this pattern in use in many places on an instance of SQL 2016 and it forces a lot of table scans. The optimizer is pessimistic on how it executes null variable and causes significant performance issues. I am going to replace them with an alternative. – Stradas Jun 09 '21 at 14:48
52

Here you go.

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
Pittsburgh DBA
  • 6,672
  • 2
  • 39
  • 68
  • 6
    Well, I would have written that as 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) – Jan de Vos Jan 12 '10 at 10:42
  • 2
    this is a great peace of example, how about the query execution plan with the best Answered ( personally I prefer this method code is clear and clean ) – PEO Aug 16 '14 at 00:42
  • 2
    really great if you want use different where clause with different type like int on 1st clause and nvarchar on the 2nd. with Bob Probst solution is does not work. thanks – Julian50 Jan 24 '15 at 08:36
7

I'd say this is an indicator of a flawed table structure. Perhaps the different location types should be separated in different tables, enabling you to do much richer querying and also avoid having superfluous columns around.

If you're unable to change the structure, something like the below might work:

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
    )

And so forth... We can't change the structure of the query on the fly, but we can override it by making the predicates equal themselves out.

EDIT: The above suggestions are of course much better, just ignore mine.

Sk8erPeter
  • 6,899
  • 9
  • 48
  • 67
Mark S. Rasmussen
  • 34,696
  • 4
  • 39
  • 58
  • I don't think this is a flawed table structure. The table was set up this way so that it was self references to have an infinite amount of parent/child relations. Believe me, it was on purpose. I don't think I want to change my table structure to just use a switch statement. its nto that important – Miles Oct 15 '08 at 22:09
6

The problem with this is that when the SQL engine goes to evaluate the expression, it checks the FROM portion to pull the proper tables, and then the WHERE portion to provide some base criteria, so it cannot properly evaluate a dynamic condition on which column to check against.

You can use a WHERE clause when you're checking the WHERE criteria in the predicate, such as

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

so in your particular case, you're going to need put the query into a stored procedure or create three separate queries.

Dillie-O
  • 29,277
  • 14
  • 101
  • 140
6

OR operator can be alternative of case when in where condition

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
Mohammad Atiour Islam
  • 5,380
  • 3
  • 43
  • 48
3

Please try this query. Answer To above post:

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
  • 3
    To whoever reads this in the future: it's the same as the accepted answer from @bob-prost above: http://stackoverflow.com/a/206500/264786 – ArturoTena Jun 19 '15 at 20:17
2

Try this:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
Sk8erPeter
  • 6,899
  • 9
  • 48
  • 67
shah134pk
  • 29
  • 1
  • 1
    Downvoted because I don't understand how this could choose between the given strings (i.e. 'location', 'area', 'division') – ArturoTena Jun 19 '15 at 20:21
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
aminography
  • 21,986
  • 13
  • 70
  • 74
0

Try this query, it's very easy and useful: Its ready to execute!

USE tempdb
GO

IF NOT OBJECT_ID('Tempdb..Contacts') IS NULL
    DROP TABLE Contacts

CREATE TABLE Contacts(ID INT, FirstName VARCHAR(100), LastName VARCHAR(100))
INSERT INTO Contacts (ID, FirstName, LastName)
SELECT 1, 'Omid', 'Karami'
UNION ALL
SELECT 2, 'Alen', 'Fars'
UNION ALL
SELECT 3, 'Sharon', 'b'
UNION ALL
SELECT 4, 'Poja', 'Kar'
UNION ALL
SELECT 5, 'Ryan', 'Lasr'
GO
 
DECLARE @FirstName VARCHAR(100)
SET @FirstName = 'Omid'
 
DECLARE @LastName VARCHAR(100)
SET @LastName = '' 
 
SELECT FirstName, LastName
FROM Contacts
WHERE  
    FirstName = CASE
    WHEN LEN(@FirstName) > 0 THEN  @FirstName 
    ELSE FirstName 
    END
AND
    LastName = CASE
    WHEN LEN(@LastName) > 0 THEN  @LastName 
    ELSE LastName
    END
GO
Omid Karami
  • 156
  • 5
0

In general you can manage case of different where conditions in this way

SELECT *
FROM viewWhatever
WHERE 1=(CASE <case column or variable>
             WHEN '<value1>' THEN IIF(<where condition 1>,1,0)
             WHEN '<value2>' THEN IIF(<where condition 2>,1,0)
             ELSE IIF(<else condition>,1,0)
         END)
fededim
  • 429
  • 5
  • 12
-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;
kavitha Reddy
  • 3,303
  • 24
  • 14
  • 4
    Downvoted because this answser is not related to the question. The question was how to use CASE on the WHERE clause, not how to use CASE on a SELECTed column. – ArturoTena Jun 19 '15 at 20:29
-1

This worked for me.

CREATE TABLE PER_CAL ( CAL_YEAR INT, CAL_PER INT ) INSERT INTO PER_CAL( CAL_YEAR, CAL_PER ) VALUES ( 20,1 ), ( 20,2 ), ( 20,3 ), ( 20,4 ), ( 20,5 ), ( 20,6 ), ( 20,7 ), ( 20,8 ), ( 20,9 ), ( 20,10 ), ( 20,11 ), ( 20,12 ), ( 99,1 ), ( 99,2 ), ( 99,3 ), ( 99,4 ), ( 99,5 ), ( 99,6 ), ( 99,7 ), ( 99,8 ), ( 99,9 ), ( 99,10 ), ( 99,11 ), ( 99,12 )

The 4 digit century is determined by the rule, if the year is 50 or more, the century is 1900, otherwise 2000.

Given two 6 digit periods that mark the start and end period, like a quarter, return the rows that fall in that range.

-- 1st quarter of 2020 SELECT * FROM PER_CAL WHERE (( CASE WHEN CAL_YEAR > 50 THEN 1900 ELSE 2000 END + CAL_YEAR ) * 100 + CAL_PER ) BETWEEN 202001 AND 202003 -- 4th quarter of 1999 SELECT * FROM PER_CAL WHERE (( CASE WHEN CAL_YEAR > 50 THEN 1900 ELSE 2000 END + CAL_YEAR ) * 100 + CAL_PER ) BETWEEN 199910 AND 199912
Mark Longmire
  • 1,160
  • 8
  • 12
-2

Try this query. Its very easy to understand:

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
Mike Clark
  • 1,860
  • 14
  • 21
  • 1
    To whoever reads this answer: it's the same as the accepted answer from @bob-prost above: http://stackoverflow.com/a/206500/264786 Downvoted for this reason. – ArturoTena Jun 19 '15 at 20:27