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

Jas*_*aty 49 sql t-sql sql-server ansi-sql sql-server-2008-r2

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

Chr*_*ndy 45

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

这些谓词适用于任何类似的类型(包括行,数组和多集),使得完全模拟它们相当复杂.但是,SQL Server不支持大多数这些类型,因此我们可以通过检查空参数/操作数来获得相当远的结果:


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

  • @Rudey,因为当只有一个操作数为 null 时,`a = b` 计算结果为 null,然后导致整个表达式计算为 null。 (2认同)
  • @costa不,SQL Server 2019仍然不支持`IS DISTINCT FROM` - 也没有**任何**方法来简洁地执行NULL安全比较(除了在谓词中使用设置操作之外,这对于标量比较是不切实际的) )。 (2认同)

小智 28

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

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

    EXISTS(SELECT a INTERSECT SELECT b)

如记录,INTERSECT对待两个空值如相等,所以如果两者都为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);
Run Code Online (Sandbox Code Playgroud)

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

  • 这应该是公认的答案,因为它以不重复对"a"和"b"的引用的方式重写谓词.对于非确定性表达式`a`和`b`,或具有副作用的表达式(例如日志记录),这将非常有用.你的第二个例子也模仿`(A.Col1,A.Col2,A.Col3)IS DISTINCT FROM(B.Col1,B.Col2,B.Col3)`,它仅由PostgreSQL本地支持(据我所知).一个非常有用的谓词,有时. (3认同)

Jas*_*aty 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))
)
Run Code Online (Sandbox Code Playgroud)

当在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)
Run Code Online (Sandbox Code Playgroud)

  • 这是一个错误的答案.请参阅克里斯回答的最后一段. (5认同)
  • @JasonKresowaty:这一点并不少见.在任何谓词中,如`(a IS DISTINCT FROM b)和某些东西`,"UNKNOWN"和"FALSE"之间的区别是必不可少的.如果`a`和`b`都是'NULL`,那么你的仿真将生成`NULL`,无论`something`是`TRUE`还是`FALSE`. (3认同)

Luk*_*zda 7

Just to extend John Keller's answer. I prefer to use EXISTS and EXCEPT pattern:

a IS DISTINCT FROM b
<=>
EXISTS (SELECT a EXCEPT SELECT b)
-- NOT EXISTS (SELECT a INTERSECT SELECT b)
Run Code Online (Sandbox Code Playgroud)

and

a IS NOT DISTINCT FROM  b
<=>
NOT EXISTS (SELECT a EXCEPT SELECT b)
-- EXISTS (SELECT a INTERSECT SELECT b)
Run Code Online (Sandbox Code Playgroud)

for one particular reason. NOT is aligned whereas with INTERSECT it is inverted.


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);
Run Code Online (Sandbox Code Playgroud)

DBFiddle Demo


小智 5

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

WHERE COALESCE(@input, x) = COALESCE(column, x)
Run Code Online (Sandbox Code Playgroud)

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

WHERE @input = column OR (@input IS NULL AND column IS NULL)
Run Code Online (Sandbox Code Playgroud)

利用的任何索引.(仅用于清晰的Parens)

  • +1提及函数如何终止索引的使用。这就是我最初来到这里的原因。 (2认同)