【问题标题】:filtered INDEX on Sql Server table causes errors during InsertSql Server 表上的过滤索引导致插入期间出现错误
【发布时间】:2021-12-25 12:32:20
【问题描述】:

我在 SQL Server 2019 中有一个表,其定义如下:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING OFF
GO

CREATE TABLE [dbo].[productionLog2](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [itemID] [binary](10) NOT NULL,
    [version] [int] NOT NULL,
 CONSTRAINT [PK_productionLog2] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

此表将记录生产的项目,它是一个检查点,以避免在version >0 的情况下生成具有重复(itemId,version) 的项目。换句话说,我们不应该有具有相同 itemIdversion 的行(此规则应仅适用于 version 大于 0 的行)。

所以我添加了以下约束作为过滤索引:

设置 ANSI_PADDING 关闭 去吧

CREATE UNIQUE NONCLUSTERED INDEX [UQ_itemID_ver] ON [dbo].[productionLog2] 
(
    [itemID] ASC,
    [version] ASC
)
WHERE ([version]>=(0))
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) ON [PRIMARY]
GO

问题是当我想执行包含多个命令的事务时,例如下面一个使用 C++ OLE API(对于 VC V7/Visual Studio 2000),在将上述索引添加到表后插入失败,尽管插入命令本身将在 SQL Server 管理工作室中单独运行而不会出现错误。

C++ 遵循这样的顺序:

--begin C++ transaction
--excute sub-command 1 in C++
SELECT ISNULL(MAX(version),-1)  
FROM [dbo].[productionLog2]         
WHERE [itemID]=0x01234567890123456789


--increase version by one inside C++ code
-- consider fox example max version is 9
-- will use 10 for next version insertion


--excute sub-command 2 in C++
INSERT INTO [dbo].[productionLog2]([itemID]          ,[version]          )          
VALUES          (0x01234567890123456789,10);


--end  C++ transaction

上面的事务到达insert命令时会运行失败,但下面的脚本第一次运行没有错误(下次运行会因为约束而失败):

INSERT INTO [dbo].[productionLog2]([itemID]          ,[version]          )          
VALUES          (0x01234567890123456789,10);

你能想象定义的约束有什么问题吗?或者是什么原因导致它会避免运行 C++ 命令但在 SSMS 中运行良好?

P.S. 在此之前,我没有要求在我的 INDEX 上添加 WHERE ([version]>=(0)),所以我使用的是 UNIQUE 约束,但由于我想过滤 CONSTRAINT,所以我将约束更改为带有过滤器的 INDEX,什么都没有在我的代码执行期间发生此更改之前出错。

【问题讨论】:

  • 您的问题不是很清楚,但添加过滤索引可能会中断没有所需 SET 选项的连接的插入。该实例中的错误消息将非常清楚并告诉您问题。例如INSERT/Update failed because the following SET options have incorrect settings: ‘QUOTED_IDENTIFIER’. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
  • 过滤索引所需的会话SET 选项在the documentation 中。这些是使用现代 API 正确设置的,但您似乎有旧代码。将此添加到您的 T-SQL 语句批处理中:SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT,CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;SET NUMERIC_ROUNDABORT OFF;
  • @DanGuzman 是的,问题是关于QUOTED_IDENTIFIER。据我了解,这些是会话参数,但是是否可以以某种方式更改特定数据库的默认 SQL 服务器会话值?除了感谢文档的链接之外,它非常有用。
  • @VSB,我添加了一个包含更多细节的答案。
  • 您的应用程序代码正在更改 QOUTED_IDENTIFIER 设置。有一个默认值,但它通常被应用程序忽略,因为它通常由连接接口设置。 TBH 您的架构无论如何都需要更正。你不可能需要一个 INT 作为版本。由于一些相当明显的假设,您可能应该有一个约束,因此它必须 >= 零。所以过滤后的唯一索引对这个假设没有多大意义。为了将来参考,当你遇到一个竞争错误时发布竞争错误 - “运行失败”不是一个有用的东西。

标签: c++ sql-server ole sql-server-2019


【解决方案1】:

过滤索引所需的会话 SET 选项在CREATE INEX documentation 中列出:

+-------------------------+----------------+----------------------+-------------------------------+--------------------------+
|       SET options       | Required value | Default server value | Default OLE DB and ODBC value | Default DB-Library value |
+-------------------------+----------------+----------------------+-------------------------------+--------------------------+
| DB-Library value        |                |                      |                               |                          |
| ANSI_NULLS              | ON             | ON                   | ON                            | OFF                      |
| ANSI_PADDING            | ON             | ON                   | ON                            | OFF                      |
| ANSI_WARNINGS*          | ON             | ON                   | ON                            | OFF                      |
| ARITHABORT              | ON             | ON                   | OFF                           | OFF                      |
| CONCAT_NULL_YIELDS_NULL | ON             | ON                   | ON                            | OFF                      |
| NUMERIC_ROUNDABORT      | OFF            | OFF                  | OFF                           | OFF                      |
| QUOTED_IDENTIFIER       | ON             | ON                   | ON                            | OFF                      |
+-------------------------+----------------+----------------------+-------------------------------+--------------------------+

这些已由现代 SQL Server API 正确设置,但您似乎有旧代码和/或驱动程序。

将这些 SET 语句添加到修改具有过滤索引的表的 T-SQL 批处理中:

SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT,CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;
SET NUMERIC_ROUNDABORT OFF;

在完全没有设置会话选项的过时驱动程序的情况下,将使用默认数据库SET 选项。这些大多设置为OFF 以实现向后兼容性。下面的脚本将设置过滤索引所需的数据库默认值,但同样,驱动程序或会话的显式设置将覆盖这些。

ALTER DATABASE YourDatabase SET ANSI_NULLS ON;
ALTER DATABASE YourDatabase SET ANSI_PADDING ON;
ALTER DATABASE YourDatabase SET ANSI_WARNINGS ON;
ALTER DATABASE YourDatabase SET ARITHABORT ON;
ALTER DATABASE YourDatabase SET CONCAT_NULL_YIELDS_NULL ON;
ALTER DATABASE YourDatabase SET QUOTED_IDENTIFIER ON;
ALTER DATABASE YourDatabase SET NUMERIC_ROUNDABORT OFF;

【讨论】:

    猜你喜欢
    • 2021-07-30
    • 1970-01-01
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 2012-02-29
    • 2020-03-22
    • 2019-06-19
    • 2017-05-30
    相关资源
    最近更新 更多