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)
的项目。换句话说,我们不应该有具有相同 itemId
和 version
的行(此规则应仅适用于 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;
我在 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)
的项目。换句话说,我们不应该有具有相同 itemId
和 version
的行(此规则应仅适用于 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;