【问题标题】:How to use CASE statement inside a WHERE with an IN clause?如何在带有 IN 子句的 WHERE 中使用 CASE 语句?
【发布时间】:2019-05-15 17:52:11
【问题描述】:

我正在尝试执行以下操作(伪代码,因为它无法编译):

declare showListedOrSold  int = 1 -- get value from config table

select *
from table
where CASE WHEN @showListedOrSold = 0 THEN id IN (1, 2, 5, 6, 10, 11) 
             WHEN @showListedOrSold = 1 THEN id IN (1, 5, 6, 10, 11) 
             WHEN @showListedOrSold = 2 THEN id IN (2) 
      END

基本上取决于showListedOrSold 的值,它应该带回某些id 值。

该语句不喜欢 IN 子句。这个用例的正确语法是什么?

【问题讨论】:

  • 远离 CASE 解决方案,因为它们不是 SARGable 并且会产生性能问题。

标签: sql sql-server sql-server-2016


【解决方案1】:

如果您想要一个“优雅的解决方案”,您可能需要考虑将这些值存储在可用于查询的表中。

CREATE TABLE #ValidIds(
    ShowListedOrSold int,
    id int);

INSERT INTO #ValidIds
VALUES( 0, 1),
      ( 0, 2),
      ( 0, 5),
      ( 0, 6),
      ( 0, 10),
      ( 0, 11),
      ( 1, 1),
      ( 1, 5),
      ( 1, 6),
      ( 1, 10),
      ( 1, 11),
      ( 2, 2);

SELECT *
FROM table t
JOIN #ValidIds v ON t.id = v.id
AND v.ShowListedOrSold = @ShowListedOrSold;

【讨论】:

  • 如果您已经有一个存储ShowListedOrSold 的配置表,您可以创建此映射表,并将ShowListedOrSold 值(在配置表中)设置为映射表的FK。
  • 这对我的情况来说有点矫枉过正。此查询仅执行一次,并且此数字组合不会在任何地方重复使用。但仍然是优雅的解决方案。
  • 优点是,如果你有一个永久表,你可以简单地在那里添加值,而不是在有更多 id 可用时更改代码。
【解决方案2】:

你可以用 OR 代替 CASE 来解决这个问题:

SELECT *
FROM table
WHERE (@showListedOrSold = 0 AND id IN (1, 2, 5, 6, 10, 11))
        OR (@showListedOrSold = 1 AND id IN (1, 5, 6, 10, 11))
        OR (@showListedOrSold = 2 AND id IN (2))

【讨论】:

  • 我知道。这就是我拥有它的方式,但它并不优雅。
  • 将id与showlistedorsold关系存储在表中会更优雅,然后你可以加入: FROM table INNER JOIN newtable on table=id = newtable.id and @showListedOrSold = newtable.showListedOrSold
  • 会,但感觉有点矫枉过正,因为我只需要在我的存储过程中执行一次此查询。
  • 然后按照 Luis Cazares 的建议使用表变量或临时表。
【解决方案3】:

这开始变得相当复杂。我可能会推荐join 方法:

select t.*
from table t join
     (values (0, 1), (0, 2), (0, 5), (0, 6), (0, 10), (0, 11),
             (1, 1), (1, 5), (1, 6), (1, 10), (1, 11),
             (2, 2)
     ) v(slos, id)
     on t.slos = @showListedOrSold and
        v.id = s.id

如果变量是null,您可以轻松展开它以显示所有行:

     on (t.slos = @showListedOrSold and
         v.id = s.id
        ) or
        @showListedOrSold is null

【讨论】:

    【解决方案4】:

    case 的工作方式,你必须返回一个值,然后检查该值。

    declare @showListedOrSold int = 1 -- get value from config table
    
    select  *
    from    [table]
    where   (case
                when @showListedOrSold = 0 and  id in (1, 2, 5, 6, 10, 11) then 1
                when @showListedOrSold = 1 and  id in (1, 5, 6, 10, 11) then 1
                when @showListedOrSold = 2 and  id in (2) then 1
            else 0
            end) = 1
    

    【讨论】:

      【解决方案5】:

      你可以构造 SQL 语句然后执行它:

      declare  @showListedOrSold Int = 1
      Declare @vSQL VarChar(200)
      
      Set @vSQL = 'select * from #tbl where id In '
      Set @vSQL = @vSQL + 
                          CASE WHEN @showListedOrSold = 0 THEN '(1, 2, 5, 6, 10, 11)'
                               WHEN @showListedOrSold = 1 THEN '(1, 5, 6, 10, 11)' 
                               WHEN @showListedOrSold = 2 THEN '(2)'
                          END
      
      Execute(@vSQL)
      

      【讨论】:

        【解决方案6】:

        你可以像 WHERE 子句中的子查询一样给出它:

        select *
        from table
        where @showListedOrSold in (select  (CASE WHEN @showListedOrSold = 0 THEN id IN (1, 2, 5, 6, 10, 11) 
                     WHEN @showListedOrSold = 1 THEN id IN (1, 5, 6, 10, 11) 
                     WHEN @showListedOrSold = 2 THEN id IN (2) 
              END) as ABCD from table)
        

        【讨论】:

        • 我喜欢它略微递归的特性。
        • 它甚至不会编译
        • Case 是一个表达式,用于返回一个标量值。你试图用它来控制它不能做的逻辑流。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多