【发布时间】:2019-01-27 18:42:59
【问题描述】:
我现在有一个存储过程来处理我的一张表中的插入和更新事务。我仍在测试此解决方案是否存在任何潜在问题以及如何改进该过程。这个 SP 需要很少的参数,然后检查匹配的 ID 并执行插入或更新。我读过这个Article 关于primary key violation error, showing that MERGE is vulnerable to concurrency problems like a multi-statement conditional INSERT/UPDATE。似乎他们已经使用WITH (HOLDLOCK) 解决了一些问题。我是存储过程和合并世界的新手。如果这对于高交易量的应用程序来说是可靠的代码,我想在这里发表您的意见?我可能有多个用户在同一个表中插入或同时运行更新语句。在这种情况下,参数嗅探是否存在任何潜在问题?如果是我应该考虑使用OPTION (RECOMPILE) 还是仅适用于 SELECT 搜索查询?这是我的 SQL 代码示例:
USE [TestDB]
GO
/****** Object: StoredProcedure [dbo].[SaveMaster] Script Date: 08/21/2018 10:05:21 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: M, D
-- Create date: 08/21/2018
-- Description: Insert/Update Master table
-- =============================================
ALTER PROCEDURE [dbo].[SaveMaster]
@RecordID INT = NULL,
@Status BIT = NULL,
@Name VARCHAR(50) = NULL,
@Code CHAR(2) = NULL,
@ActionDt DATETIME = NULL,
@ActionID UNIQUEIDENTIFIER = NULL
AS
MERGE dbo.Master WITH (HOLDLOCK) AS Target
USING (SELECT @RecordID,@Status,@Name,@Code,@ActionDt,@ActionID)
AS Source (RecordID,Status,Name,Code,ActionDt,ActionID)
ON Target.RecID = Source.RecordID
WHEN MATCHED THEN
UPDATE
SET Target.Status = Source.Status,
Target.Name = Source.Name,
Target.Code = Source.Code,
Target.ActionDt = Source.ActionDt,
Target.ActionID = Source.ActionID
WHEN NOT MATCHED THEN
INSERT(
Status,Name,Code,ActionDt,ActionID
)VALUES(
Source.Status,
Source.Name,
Source.Code,
Source.ActionDt,
Source.ActionID
);
RETURN @@ERROR;
这是我如何使用服务器端语言(ColdFusion 2016)调用存储过程的示例:
<cftransaction action="begin">
<cftry>
<cfstoredproc procedure="SaveMaster" datasource="#dsn#">
<cfprocparam dbvarname="@RecordID" value="#trim(arguments.frm_recid)#" cfsqltype="cf_sql_integer" null="#!len(trim(arguments.frm_recid))#" />
<cfprocparam dbvarname="@Status" value="#trim(arguments.frm_status)#" cfsqltype="cf_sql_bit" null="#!len(trim(arguments.frm_status))#" />
<cfprocparam dbvarname="@Name" value="#trim(arguments.frm_name)#" cfsqltype="cf_sql_varchar" maxlength="50" null="#!len(trim(arguments.frm_name))#" />
<cfprocparam dbvarname="@Code" value="#trim(frm_code)#" cfsqltype="cf_sql_char" maxlength="2" null="#!len(trim(frm_code))#" />
<cfprocparam dbvarname="@ActionDt" value="#trim(NOW())#" cfsqltype="cf_sql_datetime" />
<cfprocparam dbvarname="@ActionID" value="#trim(SESSION.UserID)#" cfsqltype="cf_sql_idstamp" null="#!len(trim(SESSION.UserID))#" />
<cfprocresult name="MasterResult"/>
</cfstoredproc>
<cfset local.fnResults = {status : "200", message : "Record successully saved!", RecID : MasterResult.RecID}>
<cfcatch type="any">
<cftransaction action="rollback" />
<cfset local.fnResults = {status : "400", message : "Error! Please contact your administrator."}>
</cfcatch>
</cftry>
</cftransaction>
如您所见,我希望存储过程返回应该返回的RecID(与我在存储过程中为现有记录传递的 ID 相同,或者如果不存在,那么将像这样为 Insert @ 987654328@ 或像这样更新SELECT @RecordID AS RecID)。如果有人有任何建议并知道从运行插入/更新并合并的 SP 返回 RecID 的最佳方式,请告诉我。
【问题讨论】:
-
我发现顶部的叙述有点难以拆开,所以我看不出那里是否有问题(如果有,你能更清楚地把事情分成几段,让清楚你在问什么)。我已经解决了您似乎在底部询问的问题。如果顶部实际上是一个问题并且与您在底部提出的问题无关,我建议将其分成两个问题。 (请把这个放在底部,因为我已经回答过了)
标签: sql-server sql-server-2008 stored-procedures merge sql-merge