反转可以返回 UNKNOWN 的布尔表达式

Hei*_*nzi 11 sql-server condition

例子

我有一张桌子

ID  myField
------------
 1  someValue
 2  NULL
 3  someOtherValue
Run Code Online (Sandbox Code Playgroud)

和一个 T-SQL 布尔表达式,它可以计算为 TRUE、FALSE 或(由于 SQL 的三元逻辑)UNKNOWN:

SELECT * FROM myTable WHERE myField = 'someValue'

-- yields record 1
Run Code Online (Sandbox Code Playgroud)

如果我想获得所有其他记录,我不能简单地否定表达式

SELECT * FROM myTable WHERE NOT (myField = 'someValue')

-- yields only record 3
Run Code Online (Sandbox Code Playgroud)

我知道为什么会发生这种情况(三元逻辑),并且我知道如何解决这个特定问题。

我知道我可以使用myField = 'someValue' AND NOT myField IS NULL并且我得到一个永远不会产生 UNKNOWN 的“可逆”表达式:

SELECT * FROM myTable WHERE NOT (myField = 'someValue' AND myField IS NOT NULL)

-- yields records 2 and 3, hooray!
Run Code Online (Sandbox Code Playgroud)

一般情况

现在,让我们谈谈一般情况。假设myField = 'someValue'我有一些涉及许多字段和条件的复杂表达式,也许是子查询:

SELECT * FROM myTable WHERE ...some complex Boolean expression...
Run Code Online (Sandbox Code Playgroud)

有没有一种通用的方法来“反转”这个expession?如果它适用于子表达式,则加分:

SELECT * FROM myTable 
 WHERE ...some expression which stays... 
   AND ...some expression which I might want to invert...
Run Code Online (Sandbox Code Playgroud)

我需要支持 SQL Server 2008-2014,但如果有一个优雅的解决方案需要比 2008 年更新的版本,我也有兴趣了解它。

And*_*y M 16

您可以将条件包含在返回二进制结果的 CASE 表达式中,例如 1 或 0:

SELECT
  ...
FROM
  ...
WHERE
  CASE WHEN someColumn = someValue THEN 1 ELSE 0 END = 1
;
Run Code Online (Sandbox Code Playgroud)

否定表达式将为您提供来自同一数据源的所有其他行,包括someColumn为空的行:

SELECT
  ...
FROM
  ...
WHERE
  NOT CASE WHEN someColumn = someValue THEN 1 ELSE 0 END = 1
  -- or: CASE WHEN someColumn = someValue THEN 1 ELSE 0 END <> 1
;
Run Code Online (Sandbox Code Playgroud)

从 SQL Server 2012 开始,您还拥有IIF 函数,它只是像上面这样的二进制 CASE 的包装器。所以,这个 CASE 表达式:

CASE WHEN someColumn = someValue THEN 1 ELSE 0 END
Run Code Online (Sandbox Code Playgroud)

如果使用 IIF 重写,将如下所示:

IIF(someColumn = someValue, 1, 0)
Run Code Online (Sandbox Code Playgroud)

您可以使用与 CASE 表达式完全相同的方式来使用它。性能上没有区别,只是代码会稍微简洁一些,也可能更简洁。


Pau*_*ite 10

我想到的第一个想法是:

DECLARE @T AS table (c1 integer NULL);

INSERT @T (c1)
VALUES (1), (NULL), (2);

-- Original expression c1 = 1
SELECT T.c1
FROM @T AS T
WHERE c1 = 1;
Run Code Online (Sandbox Code Playgroud)

返回:

结果

-- Negated
SELECT T.c1
FROM @T AS T
WHERE NOT EXISTS (SELECT 1 WHERE c1 = 1);
Run Code Online (Sandbox Code Playgroud)

返回:

否定结果

这依赖的方式EXISTS总是返回truefalse,从不未知。需要的SELECT 1 WHERE是不幸的是必要的,但它可能是可行的为你的要求,例如:

DECLARE @T AS table (c1 integer NULL);

INSERT @T (c1)
VALUES (1), (NULL), (2);

-- Original expression c1 = 1
SELECT T.c1
FROM @T AS T
WHERE c1 = 1;
Run Code Online (Sandbox Code Playgroud)

请参阅EXISTS (Transact-SQL)


一个稍微复杂一些的工作示例,展示了如何应用EXISTSCASE/IIF方法来反转单个谓词:

DECLARE @T AS table 
(
    c1 integer NULL,
    c2 integer NULL,
    c3 integer NULL
);

INSERT @T 
    (c1, c2, c3)
VALUES 
    (1, NULL, 2),
    (2, 2, 3),
    (NULL, 1, 4);
Run Code Online (Sandbox Code Playgroud)

代码:

-- Original
SELECT 
    T.c1,
    T.c2,
    T.c3
FROM @T AS T
WHERE
    1 = 1
    -- Predicate #1
    AND T.c1 = 2
    -- Predicate #2
    AND T.c2 =
    (
        SELECT MAX(T2.c2)
        FROM @T AS T2
        WHERE T2.c2 IS NOT NULL
    )
    -- Predicate #3
    AND T.c3 IN (3, 4)
    ;

-- Invert predicates #1 and #2
SELECT 
    T.c1,
    T.c2,
    T.c3
FROM @T AS T
WHERE
    1 = 1
    AND NOT EXISTS (SELECT 1 WHERE 1 = 1
        -- Predicate #1
        AND T.c1 = 2)
    AND NOT EXISTS (SELECT 1 WHERE 1 = 1
        -- Predicate #2
            AND T.c2 =
            (
                SELECT MAX(T2.c2)
                FROM @T AS T2
                WHERE T2.c2 IS NOT NULL
            ))
    -- Predicate #3
    AND T.c3 IN (3, 4)
    ;
Run Code Online (Sandbox Code Playgroud)