简体   繁体   English

Sql 服务器表上的 INDEX 在插入期间导致错误

[英]INDEX on Sql Server table causes errors during Insert

I have a table in SQL Server 2019 which defined like this:我在 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

This table is going to log produced items and it is a checkpoint to avoid generation of items with duplicate (itemId,version) in case version >0.此表将记录生产的项目,它是一个检查点,以避免在version >0 的情况下生成具有重复(itemId,version)的项目。 In other words we should have no rows with same itemId and version (this rule should only apply to rows with version greater than 0).换句话说,我们不应该有具有相同itemIdversion的行(此规则应仅适用于version大于 0 的行)。

So I've added below constraint as a filtered INDEX:所以我添加了以下约束作为过滤索引:

SET ANSI_PADDING OFF GO设置 ANSI_PADDING 关闭 GO

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

The problems is when I want to execute transactions which contain several commands, such as below one using C++ OLE APIs (for VC V7/Visual Studio 2000), the insertion fails after adding above index to the table, although the insert command itself will run individually inside SQL Server management studio with no errors.问题是当我想执行包含多个命令的事务时,例如下面一个使用 C++ OLE API(对于 VC V7/Visual Studio 2000),将上述索引添加到表后插入失败,尽管插入命令本身会运行单独在 SQL 服务器管理工作室内,没有错误。

C++ follows such a sequence: 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

Above transaction will fails to run when it reaches to insert command, but below scripts runs without errors in for the first time (for next runs, it will fail due to constraint):上面的事务在到达插入命令时会运行失败,但下面的脚本第一次运行没有错误(下次运行会因为约束而失败):

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

Can you imagine is what is wrong with defined constraint?你能想象定义的约束有什么问题吗? Or what causes that it will avoid running C++ commands but is working well inside SSMS?或者是什么原因导致它将避免运行 C++ 命令但在 SSMS 中运行良好?

PS Prior to this I had no requirment to add WHERE ([version]>=(0)) on my INDEX so I was using UNIQUE constraint but since I want to have filtered CONSTRAINT I changed constraint as an INDEX with filters and nothing went wrong before this change during my code execution. PS在此之前,我不需要在我的 INDEX 上添加WHERE ([version]>=(0))所以我使用的是 UNIQUE 约束,但是由于我想过滤 CONSTRAINT 我将约束更改为带有过滤器的 INDEX 并没有出错在我的代码执行期间进行此更改之前。

The required session SET options for filtered indexes are listed in the CREATE INEX documentation : CREATE INEX 文档中列出了过滤索引所需的 session SET 选项:

+-------------------------+----------------+----------------------+-------------------------------+--------------------------+
|       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                      |
+-------------------------+----------------+----------------------+-------------------------------+--------------------------+

These are set properly by modern SQL Server APIs but it seems you have old code and/or driver.这些由现代 SQL 服务器 API 正确设置,但您似乎有旧代码和/或驱动程序。

Add these SET statements to T-SQL batches that modify tables with filtered indexes:将这些SET语句添加到修改具有过滤索引的表的 T-SQL 批处理中:

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

In the case of an outdated driver that doesn't set session options at all, the default database SET options will be used.如果过时的驱动程序根本没有设置 session 选项,将使用默认的数据库SET选项。 These are mostly set to OFF for backwards compatibility.这些大多设置为OFF以实现向后兼容性。 The script below will set the database defaults as needed but, again, an explicit setting by the driver will override these.下面的脚本将根据需要设置数据库默认值,但同样,驱动程序的显式设置将覆盖这些。

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM