如何重写IS DISTINCT FROM和不DISTINCT FROM?

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

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

这些谓词可以与任何可比较的types(包括行,数组和多重集)一起工作,使其精确地模拟它们相当复杂。 但是,SQL Server不支持这些types中的大多数,所以我们可以通过检查空参数/操作数来得到相当的效果:

  • 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 。 在这两种情况下,你的expression式都会产生Unknown

我喜欢的另一个解决scheme利用了EXISTS与INTERSECT结合的真正的二值布尔结果。 此解决scheme应该在SQL Server 2005+中工作。

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

    EXISTS(SELECT a INTERSECT SELECT b)

如文档所述,INTERSECT将两个NULL值视为相等,所以如果两者都是NULL,那么INTERSECT结果为单个行,因此EXISTS为真。

  • 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 : //sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx

如果您的SQL实现没有实现SQL标准IS DISTINCT FROMIS NOT DISTINCT FROM运算符,则可以使用以下等同性重写包含它们的expression式:

一般来说:

 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就是一个替代scheme:

 a IS DISTINCT FROM b <==> COALESCE(a, placeholder) <> COALESCE(b, placeholder) a IS NOT DISTINCT FROM b <==> COALESCE(a, placeholder) = COALESCE(b, placeholder) 

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

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

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

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

利用的任何索引。 (Parens只用于清晰)

对于引用, IS [ NOT ] DISTINCT FROM的最规范(和可读)的实现将是一个格式良好的CASEexpression式。 对于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 

显然,其他解决scheme(特别是John Keller的 ,使用INTERSECT )更简洁。

更多细节在这里

这些expression式可以很好地替代IS DISTINCT FROM逻辑,并且比以前的例子执行得更好,因为它们最终被SQL服务器编译成一个谓词expression式, filterexpression式的一半操作员成本。 它们与Chris Bandy提供的解决scheme基本相同,但它们使用嵌套的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

 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)