过滤的唯一索引导致更新失败,因为不正确的 'QUOTED_IDENTIFIER' 设置

Posted

技术标签:

【中文标题】过滤的唯一索引导致更新失败,因为不正确的 \'QUOTED_IDENTIFIER\' 设置【英文标题】:Filtered Unique Index causing UPDATE to fail because incorrect 'QUOTED_IDENTIFIER' settings过滤的唯一索引导致更新失败,因为不正确的 'QUOTED_IDENTIFIER' 设置 【发布时间】:2016-12-21 05:42:18 【问题描述】:

我们在 SQL Server 2016 数据库中的表上设置了以下过滤索引:

    CREATE UNIQUE NONCLUSTERED INDEX [fix_SystemPKeyExecutionOrder] ON [DataInt].[TaskMaster]
(
    [SystemPkey] ASC,
    [ExecutionOrder] ASC
)
WHERE ([ExecutionOrder] IS NOT NULL)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95)
GO

这导致 SQL 代码现在失败并出现以下错误:

更新失败,因为以下 SET 选项不正确 设置:'QUOTED_IDENTIFIER'。验证 SET 选项是否正确 与索引视图和/或计算列上的索引一起使用和/或 过滤索引和/或查询通知和/或 XML 数据类型 方法和/或空间索引操作。 [SQLSTATE 42000](错误 1934)。步骤失败。

过滤后的索引去掉后,代码运行完美。

查看MSDN for Index Options,没有关于 QUOTED_IDENTIFIERS 的内容。

我们的 SQL 代码中的任何 UPDATE 语句都没有任何值的双引号。我们可以看到的唯一双引号如下:

SET @ROWCOUNT = @@ROWCOUNT

    If (@ROWCOUNT = 0)
    BEGIN
        RAISERROR('The "File Import" task ACTIVE_YN could not be updated to "Y". Either the task does not exist or the system "File Import To Stage" does not exist.', 16, 1)
    END
    ELSE
    BEGIN
        Print 'Successfully updated the "File Import" task ACTIVE_YN to "Y".'
    END

即使我们将这些双引号 " 更改为两个单引号 '',代码仍然会失败并出现相同的错误。

表本身是通过以下方式创建的:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [DataInt].[TaskMaster](
    [Pkey] [bigint] IDENTITY(1,1) NOT NULL,
    [ScheduleMasterPkey] [int] NOT NULL,
    [SystemPkey] [int] NOT NULL,
    [SourcePkey] [int] NOT NULL,
    [TargetPkey] [int] NOT NULL,
    [TaskName] [varchar](255) NOT NULL,
    [TaskTypePkey] [int] NOT NULL,
    [Active_YN] [char](1) NOT NULL,
    [ModifiedDate] [datetime] NULL,
    [ModifiedBy] [varchar](100) NULL,
    [RowVersion] [timestamp] NOT NULL,
    [ExecutionOrder] [int] NULL,
 CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED 
(
    [Pkey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95) ON [PRIMARY],
 CONSTRAINT [uc_TaskName] UNIQUE NONCLUSTERED 
(
    [TaskName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 95) ON [PRIMARY]
) ON [PRIMARY]

GO

就像我说的,如果我们不创建过滤索引,那么整个代码都可以完美运行;它只会因索引而失败。

那么为什么过滤后的索引会突然导致我们的 SQL 被炸毁,我们该如何解决呢?

更新:这里是重现故障的一小段代码。此代码通过 SQL 代理作业运行。删除索引后,此代码按预期运行,说明任务不存在的错误:

DECLARE @ROWCOUNT INT = 0

UPDATE [DataIntegrationMaster].[DataInt].[TaskMaster]
    Set Active_YN = 'Y'
    where TaskName = 'File Import'
    and SystemPkey = 0

    SET @ROWCOUNT = @@ROWCOUNT

    If (@ROWCOUNT = 0)
    BEGIN
        RAISERROR('The "File Import" task ACTIVE_YN could not be updated to "Y". Either the task does not exist or the system "File Import To Stage" does not exist.', 16, 1)
    END
    ELSE
    BEGIN
        Print 'Successfully updated the "File Import" task ACTIVE_YN to "Y".'
    END

UPDATE2 的答案: 正如下面有用的答案所指出的,我不得不提出

SET QUOTED_IDENTIFIER ON

位于 SQL 的顶部以使其正常工作。

SET QUOTED_IDENTIFIER ON

当我使用它创建索引时没有效果。

【问题讨论】:

您是否能够创建一个最小但自包含的脚本,我们可以使用它来重现问题? 用代码 sn-p 更新了导致错误的问题。 【参考方案1】:

正如@Roger Wolf 的回答中指出的那样,创建过滤索引需要您将QUOTED_IDENTIFER 设置设置为ON,这就是您所做的。如果您不这样做,您一开始就无法创建过滤索引。

但是,一旦创建,似乎对该表的任何 DML 操作(不仅仅是更新)都需要您将 QUOTED_IDENTIFER 设置也设置为 ON。这是您目前缺少的内容,也是您收到错误的原因。

所以,我不知道您的 update 的上下文是什么,无论您是作为临时语句运行它,还是它是存储过程的一部分。无论哪种方式,请确保在开头的某处包含 SET QUOTED_IDENTIFIER ON 语句。

【讨论】:

感谢您的更新。我对你投了赞成票,但给了@Roger 答案功劳,因为他首先获得了与信息的链接。正如您所说,我必须将SET QUOTED_IDENTIFIER ON 放入SQL 中。该 SQL 是一项常规代理工作。我发现现在编写的任何涉及该表的 SP 或其他 SQL 都必须明确设置 SET QUOTED_IDENTIFIER ON,这很烦人。【参考方案2】:

有:SET QUOTED_IDENTIFIER (Transact-SQL)

为了防止出现类似问题,我建议检查创建过滤索引的确切要求:CREATE INDEX (Transact-SQL)。它有一个漂亮整洁的表格,显示了创建过滤索引所需的SET 选项。

【讨论】:

良好的链接@Roger。我想知道为什么 MSDN 不能将这些信息与 OP 链接到的页面放在同一页面上。 我必须将SET QUOTED_IDENTIFIER ON 放在实际执行更新的 SQL 的顶部;如果我把SET QUOTED_IDENTIFIER ON 放在索引的创建中它没有效果。现在它正在工作。谢谢你的链接。那是一个 MSDN 很好的链接。我发现现在编写的任何涉及该表的 SP 或其他 SQL 都必须明确设置 SET QUOTED_IDENTIFIER ON,这很烦人。 您可以尝试在服务器级别默认设置此选项ON,从而最大限度地减少对现有代码的影响。除非此选项在某处明确设置为OFF,否则每个人都会选择服务器默认值。

以上是关于过滤的唯一索引导致更新失败,因为不正确的 'QUOTED_IDENTIFIER' 设置的主要内容,如果未能解决你的问题,请参考以下文章

创建唯一索引似乎失败了,但是还是创建了吗?

MySQL:数据存在则更新,不存在则插入

mysql如何避免主键或者唯一索引重复导致的插入失败问题

mysql过滤重复数据的问题

每日一记--索引/过滤器

由于在同一 SaveChanges 中删除的记录中的值,实体更新在唯一索引上失败