【问题标题】:SQL Server 2008R2: SET ANSI_NULLS OFF does not affect merge matching with null valuesSQL Server 2008R2:SET ANSI_NULLS OFF 不影响与空值的合并匹配
【发布时间】:2019-05-22 05:19:22
【问题描述】:

我已经阅读了在当前会话中使用“SET ANSI_NULLS OFF”以便能够将 NULL = NULL 评估为真,例如以下示例显示了 ANSI_NULLS ON 和 ANSI_NULLS OFF 之间的区别:

查询 A:

SET ANSI_NULLS OFF

IF(NULL = NULL)
    SELECT 'NULL = NULL'
ELSE 
    SELECT 'NO MATCH'

结果:'NULL = NULL'

查询 B:

SET ANSI_NULLS ON

IF(NULL = NULL)
    SELECT 'NULL = NULL'
ELSE 
    SELECT 'NO MATCH'

结果:'不匹配'

所以这显示了 ON 和 OFF 设​​置之间的区别。

当在标准选择语句的 where 子句中使用它时,这似乎也有效。

但是,当源字段和目标字段为空时,这似乎在合并中不起作用。

重现一个简单的场景:

创建测试表:

CREATE TABLE [dbo].[TestTable]
(
    [Id] [INT] IDENTITY(1,1) NOT NULL,
    [SomeText] [NVARCHAR](100) NULL,
    [Counter] [INT] NOT NULL,

    CONSTRAINT [PK_TestTable] 
        PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
GO

合并查询

MERGE INTO TestTable AS Target
USING (VALUES(NULL)) AS Source(SomeText) ON Target.SomeText = Source.SomeText

WHEN MATCHED THEN
    UPDATE SET Target.Counter = Target.Counter + 1

WHEN NOT MATCHED THEN
    INSERT (SomeText) VALUES(Source.SomeText);

如果匹配,则计数器加 1。如果不匹配,则插入新行。两次运行查询时,结果是两行,这不是我在 ansi_nulls 关闭时所期望的。

如果我将值 NULL 更改为 'test',则匹配工作正常,例如

使用(值(NULL))=>使用(值('test'))

使用合并时是否有一些特殊行为可以解释这一点?还是sql server的bug?

注意:我不是在寻找使用 ISNULL(...) 解决方案或类似解决方案的解决方法。这样我就无法确保有效使用匹配字段的索引。最初的问题是关于与多个匹配字段的合并,其中多个匹配字段可能碰巧为空。

【问题讨论】:

  • 最好不要使用SET ANSI_NULLS OFF,它已被弃用。根据documentation“在 SQL Server 的未来版本中,ANSI_NULLS 将为 ON,任何显式将该选项设置为 OFF 的应用程序都会产生错误。避免在新的开发工作中使用此功能,并计划修改当前使用此功能的应用程序。” 您实际上想在这里实现什么?如果您需要将NULLNULL 进行比较,请使用A.Column IS NULL and B.Column IS NULL
  • MERGE 语句有很多问题,Aaron Bertrand 在这里记录了这些问题:mssqltips.com/sqlservertip/3074/… 我不知道您的具体问题是否包括在这里,但我仍然鼓励您避免使用合并。
  • 我正在尝试以这种方式使用 MERGE:MERGE INTO... ... ON Target.Field1 = Source.Field1 AND Target.Field2 = Source.Field2 AND Target.Field3 = Source.Field3 ...我需要这个匹配来支持空匹配和非空匹配。我知道我可以这样做: If ( (Target.Field1 = Source.Field1) OR (Target.Field1 IS NULL AND Source.Field1 IS NULL) ) @larnu 但是这样我预测查询计划在所有情况下都不是有效的与 field1、field2 和 field3 的空值和非空值混合。在这种情况下,您如何建议有效匹配?
  • @JoeStefanelli 是的,我知道这一点,只需要在用简单的 SELECT + UPDATE/INSERT 语句替换之前确认这一点。
  • SET ANSI_NULLS ON affects a comparison only if one of the operands of the comparison is either a variable that is NULL or a literal NULL. If both sides of the comparison are columns or compound expressions, the setting does not affect the comparison.

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


【解决方案1】:

仅解决方法!

如果您想处理空值,您可以将 ON 条件从 Target.SomeText = Source.SomeText 更改为 IS NOT DISTINCT FROM 等效项:

MERGE INTO TestTable AS Target
USING (VALUES(NULL)) AS Source(SomeText)
ON EXISTS (SELECT Target.SomeText INTERSECT SELECT Source.SomeText)
WHEN MATCHED THEN
UPDATE SET Target.Counter = ISNULL(Target.Counter,1) + 1
WHEN NOT MATCHED THEN
INSERT (SomeText) VALUES(Source.SomeText);

db<>fiddle demo

我同意 cmets 的观点,您应该避免使用 SET ANSI_NULLS OFF,因为它已被弃用。

【讨论】:

  • 匹配必须有效使用索引
  • @StephanRyer - 你检查过执行计划看它不是吗? SQL Server 可以理解这种模式并使用它进行搜索。见sqlkiwi.blogspot.com/2011/06/…
  • 实际上,我检查了它,当使用显式索引提示时,它似乎正确使用了索引。使用索引进行良好搜索:... WHERE(列 = @值或(列为空且@值为空))。性能不佳无法使用索引:... WHERE ISNULL(ColumnField, '') = ISNULL(@ value, '') 后一种情况是索引会效率低下。
【解决方案2】:

SET ANSI_NULLS 仅在非常有限的情况下影响NULL 比较的语义。具体来说

仅当比较的操作数之一为 要么是 NULL 变量,要么是文字 NULL。如果两边 比较是列或复合表达式,设置不 影响比较。 (source)

当您将文字 NULL 包装在派生表中时,此条件不再满足,因此预计此设置不会如您所愿。

【讨论】:

    猜你喜欢
    • 2019-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-21
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多