Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/drupal/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 当表上存在外键时发生死锁_Sql Server_Database_Foreign Keys_Deadlock - Fatal编程技术网

Sql server 当表上存在外键时发生死锁

Sql server 当表上存在外键时发生死锁,sql-server,database,foreign-keys,deadlock,Sql Server,Database,Foreign Keys,Deadlock,DB中有两个表,Audit和AuditField,下面是创建表代码: -- Primary key: ID CREATE TABLE [dbo].[Audit]( [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY, [TypeName] [varchar](50) NOT NULL ) GO -- Primary key: ID CREATE TABLE [dbo].[AuditField]( [ID] [int] IDENTITY(1,1) NOT

DB中有两个表,Audit和AuditField,下面是创建表代码:

-- Primary key: ID
CREATE TABLE [dbo].[Audit](
[ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[TypeName] [varchar](50) NOT NULL
) 
GO

-- Primary key: ID
CREATE TABLE [dbo].[AuditField](
[ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[AuditID] [int] NOT NULL,
[Field1] [varchar](50) NOT NULL
)

GO
-- Set foreign key on AuditField table 
ALTER TABLE [dbo].[AuditField] 
ADD  CONSTRAINT [FK_AuditFiled_Audit] FOREIGN KEY([AuditID])
REFERENCES [dbo].[Audit] ([ID])
GO
然后我准备了一些测试数据:

DECLARE @audit TABLE
(
ID int not null,
TypeName varchar(50)
)

DECLARE @auditField TABLE
(
AuditID int not null,
Field1 varchar(50)
)

-- ADD TEST DATA
DECLARE @i int = 1
DECLARE @rowCount int = 500
WHILE @i<=@rowCount
BEGIN
INSERT INTO @audit
VALUES(@i, 'SomeTypeName')

INSERT INTO @auditField
(AuditID,Field1)
VALUES(@i,'SomeThing')

SET @i += 1
END
当此事务并发运行时,出现死锁,一个进程失败,另一个进程出错:

Msg 547,16级,状态0,第40行INSERT语句冲突 使用外键约束“FK_Audit\u Audit”。冲突 发生在数据库“MyDB”、表“dbo.Audit”、列“ID”中

Audit和AuditField表上没有触发器。 抱歉的代码格式,我真的需要一个答案为什么这个死锁发生,谢谢

有一点应该很清楚,AuditField表的数据来自@AuditField,因为@Bogdan回答我重写如下:

begin transaction
INSERT INTO dbo.Audit
OUTPUT inserted.ID INTO @temp
SELECT TypeName
FROM @audit

INSERT INTO @idMapping
SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RowNumber, ID
FROM @temp

INSERT INTO dbo.AuditField
SELECT m.ID AS AuditID, Field1
FROM @auditField af
INNER JOIN @idMapping m ON af.AuditID = m.RowNumber

commit transaction

您试图在dbo.AuditField中插入无效的外键值:

SELECT AuditID+@offSet AS AuditID, Field1

为什么是@offset?您不必在dbo.Audit表中使用该值进行审核。

这是读写死锁:

如您所见,每个事务都已成功获取[e]X[clusive]锁,并请求S[hared]锁。问题是为什么一个事务试图读取被另一个事务锁定的X行

答案如下: 1) 下面是一段源代码

declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount
假设由每个

INSERT INTO dbo.Audit
SELECT TypeName
FROM ...
声明仍在继续。这是完全错误的,如下图所示:

这意味着在某个时间点,事务可以成功地在插入的行上获得X个锁,然后 1) 因为插入到审核中的行不是连续的且 2) 因为

declare @lastIdentity int = @@identity
declare @offSet int = @lastIdentity - @rowCount

INSERT INTO dbo.AuditField
SELECT AuditID+@offSet AS AuditID, Field1 ...
最后一个
INSERT
尝试将属于另一个事务的
audited
值插入
dbo.AuditField
,这需要
FK
验证,也意味着SQL Server需要从
dbo.Audit
读取行。为此,需要S[共享]锁

需要明确的是:此死锁的根本原因不是FK约束。真正的问题是源代码

解决方案:我将改写如下:

begin transaction
INSERT INTO dbo.Audit
OUTPUT inserted.ID, inserted.TypeName INTO @audit (ID, TypeName)
SELECT TypeName
FROM @audit
-- ORDER BY ID -- Isn't necessary 

... do something (ex. DELETE) with rows from @audit

INSERT INTO dbo.AuditField (AuditID, ...)
SELECT x.ID, ...
FROM @audit x
-- ORDER BY AuditID 

/* or 
    INSERT INTO dbo.AuditField (AuditID, Field1, ....)
    SELECT y.ID, y.ColumnName, ...
    FROM (
        SELECT x.ID, ...
        FROM @audit x
        UNPIVOT( ColumnValue FOR ColumnName IN ([TypeName], ...) )
    ) y
    WHERE y.....
*/
commit transaction -- Isn't necessary

谢谢你的回答,我知道为什么会出现这种僵局。关于你的解决方案还有一个问题,使用输出我可以插入ID,是否有一种将ID映射到@auditField.Audited的示例方法?我希望保持Audit和auditField表之间的映射。现在应该可以了,因为
INSERT INTO dbo.auditField…
将只在同一事务中插入
INSERT INTO dbo.Audit…
生成的
Audited
值。
begin transaction
INSERT INTO dbo.Audit
OUTPUT inserted.ID, inserted.TypeName INTO @audit (ID, TypeName)
SELECT TypeName
FROM @audit
-- ORDER BY ID -- Isn't necessary 

... do something (ex. DELETE) with rows from @audit

INSERT INTO dbo.AuditField (AuditID, ...)
SELECT x.ID, ...
FROM @audit x
-- ORDER BY AuditID 

/* or 
    INSERT INTO dbo.AuditField (AuditID, Field1, ....)
    SELECT y.ID, y.ColumnName, ...
    FROM (
        SELECT x.ID, ...
        FROM @audit x
        UNPIVOT( ColumnValue FOR ColumnName IN ([TypeName], ...) )
    ) y
    WHERE y.....
*/
commit transaction -- Isn't necessary