【问题标题】:How to create a ternary condition on a bit field in T-SQL如何在 T-SQL 中的位字段上创建三元条件
【发布时间】:2011-10-11 20:20:05
【问题描述】:

我有一个 SQLExpress 表,其中包含一个用于存储 TRUE/FALSE 状态的位字段。

类似:

+----+---------+
| ID | IsAlive |
+----+---------+
| 1  |    1    |
| 2  |    0    |
| 3  |   NULL  |
| 4  |    1    |
+----+---------+

以该表为例,我想创建一个存储过程来执行以下任一操作:

  1. 检索所有记录。
  2. 仅检索带有IsAlive=1 的记录。
  3. 仅检索带有IsAlive=0 or NULL 的记录。

我正在考虑如何在不编写 IF/ELSE 条件的情况下创建我的查询 - 在我看来,有一种比这样做更好/更简洁的方法:

-- The ternary logic...
-- 0 or NULL retrieves records where IsAlive = 0 or NULL
-- 1 retrieves records where IsAlive = 1
-- Otherwise return all records 

-- sproc .....

    @IsAlive tinyint = 2 -- Return all records by default

    AS
    BEGIN
      IF(@SentToNTService = 0 OR @SentToNTService = 1)
       BEGIN
        SELECT *
        FROM MyTable
        WHERE IsAlive = @IsAlive;
       END
    ELSE -- Lame redundancy 
       BEGIN
        SELECT *
        FROM MyTable
       END  
    END

是否有另一种方法可以创建相同的结果而不必像上面那样创建两个不同的查询?

【问题讨论】:

  • 您脚本的注释部分与您的描述略有不一致。特别是,评论说检索IsAlive = 0 的记录,但描述说的是IsAlive = 0 or NULL 的行。
  • @Andriy - 感谢您的鹰眼;) .. og 帖子已更新。

标签: sql tsql sql-server-2008 conditional-statements ternary


【解决方案1】:

2 条建议:

假设你的变量@isalive 也被声明为“位”(应该是)

SELECT * FROM @t
WHERE @isalive is null or @isalive = coalesce(isalive, 0)

如果您想使用不需要 @isalive 为“位”的“位比较”解决方案(它适用于位和 tinyint)

SELECT * FROM @t
WHERE coalesce((1-coalesce(isalive, 0)) ^ @isalive, 1) > 0

第二种解决方案适用于像我这样的书呆子。一些铁杆人可能会觉得它很有趣(或者至少很有趣),因为我认为它提供了最好的性能(如果我错了,请有人纠正我)。这是一个强大的解决方案,但难以阅读。

【讨论】:

  • +1,既是为了第一个解决方案的简单性,也是为了第二个解决方案的疯狂。你知道你也可以用^ 代替- 吗?
  • @Andriy M 既然你提到了它。我没有考虑过,如果我有我肯定会使用它。我想位运算比计算略快
【解决方案2】:

这样的事情也应该有效。

SELECT *        
FROM MyTable        
WHERE (@IsAlive = 0 and IsAlive=0)
OR (@IsAlive =1 and IsAlive =1)
OR (@IsAlive is null) 

【讨论】:

    【解决方案3】:

    这并不准确,但非常接近您可以做的:

    SELECT *        
    FROM MyTable        
    WHERE CASE @IsAlive
          WHEN 0 THEN IsAlive = @IsAlive
          WHEN 1 THEN IsAlive = @IsAlive
          ELSE 1=1 --dummy true value, when null or anything else
    END
    

    【讨论】:

    • 应该非常接近,可能需要稍作调整。
    • 你不能在 case 中使用 where 子句
    【解决方案4】:

    这会做你想做的事:

        SELECT *
        FROM MyTable
        WHERE COALESCE(IsAlive, 0) = COALESCE(@IsAlive, COALESCE(IsAlive, 0))
    

    基于@IsAlive 的值:

    1. 如果为 NULL,则返回所有内容(因为条件始终为真)
    2. 如果为 1,那么将返回 IsAlive = 1 的那些行
    3. 如果为 0,则返回 IsAlive = 0 或 NULL 的行

    COALESCE 是一个函数,它返回它的第一个参数,除非它是 NULL,在这种情况下它返回它的第二个参数。

    因此,如果 IsAlive 为 NULL,则 LHS 返回 0,如果 IsAlive 为 1,则返回 0 和 1。 当存储过程参数 @IsAlive 为 NULL 时,RHS 返回相同,否则仅返回 @IsAlive 参数。

    编辑: 这假设 @IsAlive 是 BIT。在 tinyint 的情况下,您可以添加 case 语句:

        SELECT *
        FROM MyTable
        WHERE COALESCE(IsAlive, 0) = CASE @IsAlive
                                        WHEN 0 THEN 0
                                        WHEN 1 THEN 1
                                        ELSE COALESCE(IsAlive, 0)
                                     END
    

    【讨论】:

    • +1 为聪明的 COALESCE 解决方案,Petar!我喜欢。但是,我正在寻找一种解决方案,该解决方案将返回任何非 1 或零的整数值的所有结果。因此,例如,如果 sproc 调用者发送一个值 555,则查询将返回所有记录。换句话说,值为 1 只会返回 IsAlive=true,值 0(或 NULL)将返回 IsAlive=false,任何其他整数值将返回所有记录。
    • 您可能应该明确指出,@IsAlive 在您的查询中应该是 bit,而不是 tinyint,就像在 OP 的示例脚本中一样。否则,你有一个聪明的条件,即使它不是 sargable。
    • @Andriy M - 确实,'@IsAlive' 应该有点意思,即使是名字也很夸张。
    【解决方案5】:

    试试这个:

    SELECT * FROM MyTable WHERE ISNULL (IsAlive, 0) = ISNULL (@IsAlive, 0)
    UNION
    SELECT * FROM MyTable WHERE ISNULL (@IsAlive, 0) > 1
    

    【讨论】:

      猜你喜欢
      • 2012-09-16
      • 1970-01-01
      • 2013-04-19
      • 1970-01-01
      • 2012-04-11
      • 2021-04-24
      • 2011-11-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多