I am using SQL Server 2008, and would like to be able to take advantage of something like mySQL's ON DUPLICATE KEY UPDATE
clause for INSERT
statements
Current legacy code does a delete and subsequent insert that is running into concurrency issues with duplicate key inserts from separate threads:
Here is the error I see in my production environment:
Violation of PRIMARY KEY constraint 'PK_Audience'. Cannot insert duplicate key in object 'dbo.Audience'.
(sp_ContentUpdate)
Primary Key:
AudienceId, VersionId
Offending SQL:
DELETE FROM dbo.Audience
WHERE VersionId = @VersionId
IF开发者_开发知识库 @AudienceXml IS NOT NULL
BEGIN
INSERT INTO dbo.Audience (
VersionId,
AudienceId,
CreatedDate,
CreatedByPersonId,
)
SELECT @VersionId,
AudienceId,
GETUTCDATE(),
@PersonId
FROM dbo.Audience
JOIN @AudienceXml.nodes('/Audiences/Audience') node(c)
ON Audience.AudienceName = c.value('@Name', 'nvarchar(50)')
END
Wrapping this TSQL in a transaction seems to either remove the concurrency issue or mask the issue by changing the timings. However, I do not think wrapping in a transaction has actually solved the concurrency.
Perhaps I am going about this wrong. Your suggestions are appreciated.
Well, Bill beat us all, but here's a sample of what it might look like:
Merge dbo.Audience As target
Using (
Select @VersionId As VersionId, AudienceId, GetUtcDate() As CreatedDate, @PersonId As CreatedByPersonId
From dbo.Audience
Join @AudienceXml.nodes('/Audiences/Audience') node(c)
On Audience.AudienceName = c.value('@Name', 'nvarchar(50)')
)
When Matched Then
Update
Set VersoinId = target.VersionId, Audience = target.AudienceId
, CreatedDate = target.CreatedDate
, CreatedByPersionId = target.CreatedByPersonId
When Not Matched Then
Insert dbo.Audience(VersionId, AudienceId, CreatedDate, CreatedByPersonId)
You should read about how to use the MERGE
statement in Microsoft SQL Server 2008. This is actually the ANSI/ISO SQL way of handling this situation (MySQL's ON DUPLICATE KEY is a proprietary MySQLism).
See docs on the MERGE
statement at MSDN.
精彩评论