【发布时间】: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) 的项目。换句话说,我们不应该有具有相同 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),在将上述索引添加到表后插入失败,尽管插入命令本身将在 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