如何重写IS DISTINCT FROM和不DISTINCT FROM?
在Microsoft SQL Server 2008R2等不支持SQL的实现中,如何重写包含标准IS DISTINCT FROM
和IS NOT DISTINCT FROM
运算符的expression式?
IS DISTINCT FROM
谓词作为SQL:1999的特征T151被引入,并且其可读否定IS NOT DISTINCT FROM
被添加为SQL:2003的特征T152。 这些谓词的目的是保证比较两个值的结果是True或False ,而不是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 FROM
和IS 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
的最规范(和可读)的实现将是一个格式良好的CASE
expression式。 对于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)