【问题标题】:How to simulate COALESCE conditions in WHERE clause如何在 WHERE 子句中模拟 COALESCE 条件
【发布时间】:2014-01-09 21:49:28
【问题描述】:

假设我有以下内容:

SELECT    * 

FROM      Xyz 

WHERE     (@a IS NULL OR a = @a) AND
          (@b IS NULL OR b = @b) AND
          (@c IS NULL OR c = @c)

我想要完成的是更改 WHERE 子句以以 COALESCE 的方式使用条件,其中它将使用第一个不为空的条件或条件用完。

用伪语言来说是这样的:

WHERE    IF @a IS NOT NULL THEN
         (
             a = @a
         )
         ELSE IF @b IS NOT NULL THEN 
         (
             b = @b
         )
         ELSE IF @c IS NOT NULL THEN 
         (
             c = @c
         )

这可能吗?

【问题讨论】:

标签: sql-server tsql sql-server-2012


【解决方案1】:

这会将 NULL 视为通配符:

SELECT *
FROM MyTable
WHERE COALESCE(
  NULLIF(@a,a),
  NULLIF(@b,b),
  NULLIF(@c,c)
) IS NULL

这将使用第一个非空条件:

SELECT *
FROM MyTable
WHERE EXISTS (
  SELECT id,value
  FROM (VALUES (1,a),(2,b),(3,c)) t1(id,value)
  INTERSECT
  SELECT TOP 1 id,comparator
  FROM (VALUES (1,@a),(2,@b),(3,@c)) t(id,comparator)
  WHERE comparator IS NOT NULL
  ORDER BY id
)

【讨论】:

  • 不错。我试图找到这种解决方案,但最终放弃了(显然为时过早)。
【解决方案2】:

可能有更优雅的方式,但这应该尊重所需的逻辑

where
(@a is not null and @a = a) or
(@a is null and @b is not null and @b = b) or
(@a is null and @b is null and @c is not null and @c = c)

可以(有点)简化为:

where
(@a = a) or
(@a is null and @b = b) or
(@a is null and @b is null and @c = c)

【讨论】:

  • 对,这基本上就是我想要的,但实际上,更优雅的方式会更好,因为我有超过 3 个条件,这会有点罗嗦。
  • @CodeMaverick:我会担心性能问题。使用OPTION(RECOMPILE) 查询提示,执行计划可以包括Index Seek(如果有适当的索引)。
【解决方案3】:

您不能将布尔表达式的结果用作 SQL Server 中的值,但可以将其替换为逻辑 1/0,如下所示:

WHERE
  CASE
              WHEN @a IS NOT NULL AND a = @a THEN 1
    ELSE CASE WHEN @b IS NOT NULL AND b = @b THEN 1
    ELSE CASE WHEN @c IS NOT NULL AND c = @c THEN 1
  END END END = 1

上面也可以这样重新排列(也许有些人会发现这样逻辑更清晰):

WHERE
  CASE
    WHEN @a IS NOT NULL AND a = @a THEN 1
    ELSE
      CASE
        WHEN @b IS NOT NULL AND b = @b THEN 1
        ELSE
          CASE
            WHEN @c IS NOT NULL AND c = @c THEN 1
          END
      END
  END = 1

【讨论】:

  • 这不是完美的解决方案,但它绝对是迄今为止最接近的。谢谢!
  • 不客气,很高兴我能帮上忙!但是您的“到目前为止”似乎暗示您希望将来获得其他可能更好的建议。我很可能错了,但如果不是,最好不接受我的回答以保持问题开放。
【解决方案4】:
/* 
Since you are dynamically changing your condition you will need to 
use Dynamic sql and and build your query dynamically something like
this......
*/

CREATE TABLE Test_Dynamic (ID INT, A INT, B INT, C INT)
INSERT INTO Test_Dynamic VALUES
(1, 100, 1000, 10000),
(2, 200, 2000, 20000),
(3, 300, 3000, 30000),
(4, 400, 4000, 40000)

DECLARE @a INT, @b INT, @c INT;
SET @a = 100;
--SET @b = 1000;
--SET @c = 10000;

DECLARE @Sql NVARCHAR(MAX);

SET @Sql = N'SELECT * FROM  Test_Dynamic  WHERE 1 = 1 '

IF (@a IS NOT NULL)
BEGIN
 SET @Sql = @Sql + N' AND A = @ar'
  IF (@b IS NOT NULL)
    BEGIN
     SET @Sql = @Sql + N' AND B = @br'
       IF (@c IS NOT NULL)
         BEGIN
         SET @Sql = @Sql + N' AND C = @cr'
         END   
    END
END

PRINT @Sql

EXECUTE sp_executesql @Sql
                      ,N'@ar INT, @br INT, @cr INT'
                      ,@ar = @a
                      ,@br = @b
                      ,@cr = @c

【讨论】:

  • 这绝对是一种方法,但我试图避免这种方法。
  • 如果@a 不为空且@b 不为空(例如),这两个条件都将被应用。所以这与在第一个非空变量之后停止不同。
  • @RaphaëlAlthaus - 这是真的。很好,我什至没有看它,因为我不想走sp_executesql路线。
  • 顺便说一句,我不是反对者,因为它可以很容易地更改为有效的 if... else if... else 解决方案
  • 只需将SELECT * FROM Xyz 位替换为SELECT 1,这将使您的脚本独立于环境。运行它并you'll see
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2012-02-25
  • 1970-01-01
  • 2012-07-26
相关资源
最近更新 更多