“ where”子句中的SQL Switch / Case


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上抱怨我的'='...

我怎样才能做到这一点?

Answers:


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中测试了我的数据库,效果很好。
鲍勃·普罗布斯特

为什么需要@?他们在做什么?
Tadej

2
@表示t-sql中的变量,如果没有@,则@locationID将被解释为列名。
Christian Sloper

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语句时,结果将略有不同-但OR语法将评估所有可能性
2015年

1
@tember即使SQL是一种过程语言,如果第一个OR为true,则表达式的其余部分也不会得到求值。由于SQL是一种声明性语言,您如何知道DBM将评估所有OR?真诚的问题,我不明白。
ArturoTena 2015年

在SQL中,表达式的其余部分确实使用OR语法求值。试试这个(它不会让我包含@符号-如果要测试它,必须纠正它):声明var varchar(5)set var ='0'选择2 / var其中var <> 0或ISNUMERIC(var)= 1。我希望退出该条件,因为var IS等于0,但是它会继续检查它是否为数字,因此,该语句返回错误。
tember

在我为每种类型的值使用不同的比较运算符的情况下,这很有帮助。
亚历克斯

更好 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 '17

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从何处(@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 2010年

2
这是一个很好的例子,如何使用最佳已回答的查询执行计划(我个人比较喜欢此方法的代码清晰明了)
PEO 2014年

1
如果您想使用不同类型的其他where子句(例如第一个子句中的int和第二个子句中的nvarchar),那真的很棒。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
    )

依此类推...我们无法即时更改查询的结构,但可以通过使谓词彼此相等来覆盖它。

编辑:以上建议当然更好,请忽略我的建议。


我不认为这是有缺陷的表结构。该表是通过这种方式设置的,因此它是具有无限数量的父/子关系的自引用。相信我,这是故意的。我认为我不想更改表结构以仅使用switch语句。它的重要意义
迈尔斯

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
以后对谁都读这本书:与上面@ bob-
prost

2

试试这个:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)

1
投票不佳,因为我不知道如何在给定的字符串(即“位置”,“区域”,“分区”)之间进行选择
ArturoTena 2015年

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
投票无效,因为此答案与问题无关。问题是如何在WHERE子句上使用CASE,而不是如何在SELECTed列上使用CASE。
ArturoTena 2015年

-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 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.