63

在不支持它们的 SQL 实现(例如 Microsoft SQL Server 2008R2)中,如何重写包含标准IS DISTINCT FROM和运算符的表达式?IS NOT DISTINCT FROM

4

9 回答 9

62

谓词作为 SQL:1999的IS DISTINCT FROM特征 T151 被引入,它的可读否定IS NOT DISTINCT FROM被添加为 SQL:2003 的特征 T152。这些谓词的目的是保证比较两个值的结果是TrueFalse,绝不是Unknown

这些谓词适用于任何可比较的类型(包括行、数组和多集),因此精确模拟它们相当复杂。但是,SQL Server 不支持这些类型中的大多数,因此我们可以通过检查空参数/操作数来获得相当大的帮助:

  • a IS DISTINCT FROM b可以改写为:

    ((a <> b OR a IS NULL OR b IS NULL) AND NOT (a IS NULL AND b IS NULL))
    
  • a IS NOT DISTINCT FROM b可以改写为:

    (NOT (a <> b OR a IS NULL OR b IS NULL) OR (a IS NULL AND b IS NULL))
    

您自己的答案是不正确的,因为它没有考虑FALSE OR NULL评估为Unknown。例如,NULL IS DISTINCT FROM NULL应该评估为False。同样,1 IS NOT DISTINCT FROM NULL应该评估为False。在这两种情况下,您的表达式都会产生Unknown

于 2013-09-08T14:29:33.810 回答
45

我喜欢的另一个解决方案是利用 EXISTS 与 INTERSECT 结合的真正的二值布尔结果。此解决方案应适用于 SQL Server 2005+。

  • a IS NOT DISTINCT FROM b可以写成:

    EXISTS(SELECT a INTERSECT SELECT b)

如文档所述,INTERSECT 将两个 NULL 值视为相等,因此如果两者都是 NULL,则 INTERSECT 会产生单行,因此 EXISTS 会产生 true。

  • a IS DISTINCT FROM b可以写成:

    NOT EXISTS(SELECT a INTERSECT SELECT b)

如果您需要在两个表中比较多个可为空的列,这种方法会更加简洁。例如,要返回 TableB 中 Col1、Col2 或 Col3 的值与 TableA 不同的行,可以使用以下命令:

SELECT *
FROM TableA A
   INNER JOIN TableB B ON A.PK = B.PK
WHERE NOT EXISTS(
   SELECT A.Col1, A.Col2, A.Col3
   INTERSECT
   SELECT B.Col1, B.Col2, B.Col3);

Paul White 更详细地解释了这种解决方法:https ://sql.kiwi/2011/06/undocumented-query-plans-equality-comparisons.html

于 2013-10-22T22:17:37.580 回答
12

如果您的 SQL 实现未实现 SQL 标准IS DISTINCT FROMIS NOT DISTINCT FROM运算符,则可以使用以下等效项重写包含它们的表达式:

一般来说:

a IS DISTINCT FROM b <==>
(
    ((a) IS NULL AND (b) IS NOT NULL)
OR
    ((a) IS NOT NULL AND (b) IS NULL)
OR
    ((a) <> (b))
)

a IS NOT DISTINCT FROM b <==>
(
    ((a) IS NULL AND (b) IS NULL)
OR
    ((a) = (b))
)

在 UNKNOWN 和 FALSE 之间的区别很重要的上下文中使用时,此答案是不正确的。不过,我认为这并不常见。请参阅@ChrisBandy 接受的答案。

如果可以识别出数据中实际上并未出现的占位符值,则COALESCE可以使用另一种方法:

a IS DISTINCT FROM b <==> COALESCE(a, placeholder) <> COALESCE(b, placeholder)
a IS NOT DISTINCT FROM b <==> COALESCE(a, placeholder) = COALESCE(b, placeholder)
于 2012-05-02T15:25:22.080 回答
8

只是为了扩展约翰凯勒的答案。我更喜欢使用EXISTSEXCEPT模式:

a IS DISTINCT FROM b
<=>
EXISTS (SELECT a EXCEPT SELECT b)
-- NOT EXISTS (SELECT a INTERSECT SELECT b)

a IS NOT DISTINCT FROM  b
<=>
NOT EXISTS (SELECT a EXCEPT SELECT b)
-- EXISTS (SELECT a INTERSECT SELECT b)

出于一个特定的原因。NOT是对齐的,而与之INTERSECT相反。


SELECT 1 AS PK, 21 AS c, NULL  AS  b
INTO tab1;

SELECT 1 AS PK, 21 AS c, 2 AS b
INTO tab2;

SELECT *
FROM tab1 A
JOIN tab2 B ON A.PK = B.PK
WHERE EXISTS(SELECT A.c, A.B
              EXCEPT
              SELECT B.c, B.b);

DBFiddle 演示

于 2018-05-10T19:43:59.733 回答
7

重写 IS DISTINCT FROM 和 IS NOT DISTINCT FROM 的一个警告是不要干扰索引的使用,至少在使用 SQL Server 时是这样。换句话说,当使用以下内容时:

WHERE COALESCE(@input, x) = COALESCE(column, x)

SQL Server 将无法使用任何包含column的索引。因此,在 WHERE 子句中,最好使用以下形式

WHERE @input = column OR (@input IS NULL AND column IS NULL)

利用column的任何索引。(括号仅用于清楚起见)

于 2013-05-24T19:30:26.907 回答
1

拼写出来使用CASE

作为参考,最规范(和可读)的实现IS [ NOT ] DISTINCT FROM将是格式良好的CASE表达式。对于IS DISTINCT FROM

CASE WHEN [a] IS     NULL AND [b] IS     NULL THEN FALSE
     WHEN [a] IS     NULL AND [b] IS NOT NULL THEN TRUE
     WHEN [a] IS NOT NULL AND [b] IS     NULL THEN TRUE
     WHEN [a] =               [b]             THEN FALSE
     ELSE                                          TRUE
END

显然,其他解决方案(特别是John Keller 的, using INTERSECT)更简洁。

更多细节在这里

DECODE如果可用,请使用

我知道这个问题是关于 SQL Server 的,但为了完整起见,Db2 和 Oracle 支持一个DECODE()功能,在这种情况下可以模拟以下内容:

-- a IS DISTINCT FROM b
DECODE(a, b, 1, 0) = 0

-- a IS NOT DISTINCT FROM b
DECODE(a, b, 1, 0) = 1
于 2014-04-30T13:10:37.803 回答
1
a IS NOT DISTINCT FROM b

可以改写为:

(a IS NOT NULL AND b IS NOT NULL AND a=b) OR (a IS NULL AND b is NULL)

a IS DISTINCT FROM b

可以改写为:

NOT (a IS NOT DISTINCT FROM b)
于 2014-08-12T10:22:12.593 回答
0

这些表达式可以很好地替代 IS DISTINCT FROM 逻辑,并且比前面的示例执行得更好,因为它们最终被 SQL Server 编译成单个谓词表达式,这将导致大约。过滤器表达式的运算符成本的一半。它们本质上与 Chris Bandy 提供的解决方案相同,但是它们使用嵌套的 ISNULL 和 NULLIF 函数来执行基础比较。

(...如果您愿意,显然 ISNULL 可以用 COALESCE 代替)

  • a IS DISTINCT FROM b可以改写为:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NOT NULL

  • a IS NOT DISTINCT FROM b可以改写为:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NULL

于 2014-05-02T00:02:01.937 回答
0

这是一个老问题,有一个新的答案。它更容易理解和维护。

-- a IS DISTINCT FROM b
CASE WHEN (a = b) OR (a IS NULL AND b IS NULL) THEN 1 ELSE 0 END = 0

-- a IS NOT DISTINCT FROM b
CASE WHEN (a = b) OR (a IS NULL AND b IS NULL) THEN 1 ELSE 0 END = 1

应该注意的是,这种替代语法IS [NOT] DISTINCT FROM适用于所有主要的 SQL 数据库(见最后的链接)。此处详细说明了此方法和替代方法

于 2019-03-21T15:27:06.517 回答