Sql 服务器 table 上的筛选索引导致插入期间出错

filtered INDEX on Sql Server table causes errors during Insert

我在 SQL Server 2019 中有一个 table 定义如下:

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

这个 table 将记录生成的项目,它是一个检查点,以避免在 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),添加以上索引后插入失败到 table,尽管插入命令本身会 运行 在 SQL Server management studio 中单独地没有错误。

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

上面的事务在到达插入命令时会失败运行,但是下面的脚本 运行s 第一次没有错误(对于下一个 运行s,它将由于限制而失败):

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

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

P.S. 在此之前我不需要在我的 INDEX 上添加 WHERE ([version]>=(0)) 所以我使用了 UNIQUE 约束但是因为我想要filtered CONSTRAINT 我将约束更改为带有过滤器的 INDEX,并且在我的代码执行期间进行此更改之前没有任何错误。

筛选索引所需的会话 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 服务器 API 正确设置,但您似乎有旧代码 and/or 驱动程序。

将这些 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;