2016-08-02 105 views
0

I 认为我遇到的问题是并发问题,但我想我不太了解SQL Server事务,以确定要做什么。外键约束冲突并发问题

我有插入新行到一个表中的存储过程,然后使用由INSERT创建的ID创建另一个表中的记录,这对RID一个外键约束:

INSERT INTO tbl_Registrations ... 

DECLARE @RID int; 
--get newly-created registration ID 
SET @RID=(SELECT MAX(RID) FROM tbl_Registrations WHERE ...); 
INSERT INTO tbl_RegistrationCertificates VALUES (@RID, 9); 

这个存储过程是从网站上的用户交互中调用的,每次我测试它时,它都可以正常工作。然而,每过一段时间,我会从我们的错误处理一封电子邮件,但以下情况除外:

System.Data.SqlClient.SqlException (0x80131904): The INSERT statement conflicted with the FOREIGN KEY constraint "FK_tbl_RegistrationCertificates_tbl_Registrations". 
The conflict occurred in ... table "dbo.tbl_Registrations", column 'RID'. 
The statement has been terminated. 

但是,无论是我的用户在点击“更新”按钮,持续,直到他们看到变化,或者即使消息显示The statement has been terminated,交易仍在进行,因为使用来自电子邮件的其他信息,我可以看到这两个表已实际更新。

我知道我不给一吨的信息在这里,但我无法找到答案的大问题是:

  1. 是否在声明的末尾分号T中做任何事 - SQL?我只注意到在INSERT语句结尾处没有分号。 (我通常会让他们摆脱习惯,但我正在开发其他人制造的系统。)
  2. 这对于了解SQL Server的人比我更了解的人是否看起来像是一个并发问题?
  3. 如果看起来像是一个并发问题,我可以使用事务来减轻它,我该怎么做?

编辑:我只是意识到SP是不是什么都抛出了错误。网站还有另一个地方做插入,而不检查是否存在RID。不过,我很高兴我提出了这些问题,因为我仍然不知道其他答案。

回答

1

T-SQL将分号视为语句分隔符,尽管并非总是需要。关于这一点的更深入的讨论可以在question中找到。

至于外键错误,您提交的存储过程可能会因为几个原因而使用一些改进。

首先,您很可能在此处使用交易。如果您不希望添加注册记录,除非还添加了注册证书记录,那么整个操作应该在单个事务中。

其次,如果同时调用存储过程,则选择标识插入列的MAX值可能很容易成为并发问题的来源。想象一下,在线程B INSERT之前,线程A INSERTS但无法运行其SELECT MAX(RID)的场景。现在线程A和B都会尝试向tbl_RegistrationCertificates插入相同的值。

SCOPE_IDENTITY()函数将为您提供此存储过程中查询生成的最新身份值的帮助。

一些更新的代码:

BEGIN TRANSACTION 
INSERT INTO tbl_Registrations ... 

DECLARE @RID int; 
--get newly-created registration ID 
SELECT @RID = SCOPE_IDENTITY() 
INSERT INTO tbl_RegistrationCertificates VALUES (@RID, 9); 
COMMIT TRANSACTION 
+0

啊'SCOPE_IDENTITY()'是不是我以前见过;这非常有帮助。显然,为了简洁起见,我留下了很多代码,但是如果基于SP的参数满足其他条件,则只会执行第二个“INSERT”。在这种情况下交易是否有意义? –

+0

还有一个问题:使用事务和批分隔符有什么区别? 'GO'与'BEGIN TRANSACTION ... COMMIT TRANSACTION'? –

+1

事务逻辑上将多个语句分组在一起,以使它们成功或失败。在我上面的代码中,如果由于某种原因'INSERT' to _tbl_RegistrationCertificates_失败,那么'INSERT' to _tbl_Registrations_也会回滚。当一组报表全部被视为一个操作(BEGIN'和'COMMIT'之间的所有内容)时,事务可帮助您保持逻辑上一致的状态。另一方面,“GO”只是告诉SQL Server工具运行一组语句。 –