如何重写IS DISTINCT FROM并且不是DISTINCT FROM?

时间:2012-05-02 15:24:37

标签: sql sql-server tsql sql-server-2008-r2 ansi-sql

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

9 个答案:

答案 0 :(得分:41)

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

这些谓词适用于任何类似的类型(包括行,数组和多集),使得完全模拟它们相当复杂。但是,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评估为未知。例如,NULL IS DISTINCT FROM NULL应评估为 False 。同样,1 IS NOT DISTINCT FROM NULL应评估为 False 。在这两种情况下,表达式都会产生未知

答案 1 :(得分:24)

我喜欢的另一个解决方案是利用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中具有与TableA不同的Col1,Col2或Col3值的行,可以使用以下内容:

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更详细地解释了这种解决方法: http://web.archive.org/web/20180422151947/http://sqlblog.com:80/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx

答案 2 :(得分:11)

如果您的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)

答案 3 :(得分:5)

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

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

SQL Server将无法使用包含的任何索引。因此,在WHERE子句中,最好使用表单

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

利用的所有索引。 (Parens仅用于清晰)

答案 4 :(得分:1)

作为参考,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's,使用INTERSECT)更简洁。

More details here

答案 5 :(得分:1)

只是为了扩展John Keller's回答。我更喜欢使用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 Demo

答案 6 :(得分:0)

这些表达式可以很好地替代IS DISTINCT FROM逻辑,并且比前面的示例表现更好,因为它们最终被SQL服务器编译成单个谓词表达式,这将导致大约。过滤器表达式的运算符成本的一半。它们与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

答案 7 :(得分:0)

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)

答案 8 :(得分: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数据库中均有效(请参见最后的链接)。 here

对此进行了详细说明