提前致谢; 我继承了一个存储过程,它增加一条记录并在一次调用中返回它的值。目的是仅返回单个值,就像Identity()列一样。 这是存储过程:
ALTER PROCEDURE [dbo].[sp_GetNextKey]
@RetVal int OUTPUT,
@Name varchar(250)
AS
UPDATE Keys
SET Key_Next = Key_Next + 1,
@RetVal = Key_Next + 1
FROM Keys
WHERE Key_Table = @Name
不幸的是,这是非常古老的代码,我无法修改应用程序来实现Identity()。这已经在生产中工作多年,仅由ColdFusion应用程序访问。它现在由C#应用程序调用,我们正在获得似乎是线程问题。我需要在SQL中而不是在CF或.NET中解决这个问题,因为在两个应用程序中的大量位置都会调用此过程。
它们返回的值相同,就像它们生活在独立环境中一样。我应该补充一点,它确实在大多数情况下按预期工作,但并非总是如此。我的猜测是,只有在每次应用程序中以完全相同的毫秒调用它时,它才会受到严重负载。
我讨厌做某种锁定,因为这个东西每小时被召唤数千次。我担心我们最终会遇到死锁问题。
为清楚起见,这里是CF和C#中的调用:
<CFFUNCTION name="getNextId" returntype="numeric" access="public">
<CFARGUMENT name="keyTableName" type="string" required="yes" >
<CFARGUMENT name="dbSource" type="string" required="yes" >
<cfstoredproc procedure="sp_GetNextKey" datasource="#ARGUMENTS.dbSource#" returnCode="No">
<cfprocparam type="OUT" CFSQLType="CF_SQL_INTEGER" variable="RetVal">
<cfprocparam type="IN" CFSQLType="CF_SQL_VARCHAR" value="#UCase(ARGUMENTS.keyTableName)#" maxlength="250">
</cfstoredproc>
<cfquery name="ab" datasource="#ARGUMENTS.dbSource#">
SET ARITHABORT ON
</cfquery>
<cfreturn RetVal >
</CFFUNCTION>
C#:
SqlCommand cmd = new SqlCommand("sp_GetNextKey", conn);
cmd.CommandType = CommandType.StoredProcedure;
var outParam = new SqlParameter("@RetVal", SqlDbType.Int);
outParam.Direction = ParameterDirection.Output;
outParam.Size = 128;
cmd.Parameters.Add(outParam);
cmd.Parameters.Add("@Name", SqlDbType.VarChar);
cmd.Parameters["@Name"].Value = tableName;
conn.Open();
cmd.ExecuteNonQuery();
//get the return value
retVal = Convert.ToInt32(cmd.Parameters["@RetVal"].Value);
conn.Close();
允许此事件为任何调用它的应用程序返回唯一值的最佳方法是什么?
根据评论,我们尝试了这一点,但没有改变结果:
ALTER
PROCEDURE [dbo].[sp_GetNextKey]
@RetVal int OUTPUT,
@Name varchar(250)
AS
UPDATE Keys
WITH (ROWLOCK)
SET @RetVal = Key_Next = Key_Next + 1
FROM Keys
WHERE Key_Table = @Name
也试过但没有用:
ALTER
PROCEDURE [dbo].[sp_GetNextKey]
@RetVal int OUTPUT,
@Name varchar(250)
AS
BEGIN TRANSACTION
EXEC sp_getapplock @LockMode = 'Shared', @Resource = 'Keys';
UPDATE Keys
WITH (ROWLOCK)
SET @RetVal = Key_Next = Key_Next + 1
FROM Keys
WHERE Key_Table = @Name
EXEC sp_releaseapplock @Resource = 'Keys'
COMMIT TRANSACTION
答案 0 :(得分:2)
试试这个:
ALTER PROCEDURE [dbo].[sp_GetNextKey]
@RetVal int OUTPUT,
@Name varchar(250)
AS
BEGIN TRAN
SELECT @RetVal = MAX(Key_Next) + 1 FROM Keys WHERE Key_Table = @Name
UPDATE Keys SET Key_Next = @RetVal WHERE Key_Table = @Name
COMMIT
这有意将两个操作拆分为单独的语句,并将它们包装在一个事务中,以便使用正确的锁并保持一致性。
答案 1 :(得分:1)
试试吧
ALTER PROCEDURE [dbo].[sp_GetNextKey]
@RetVal int OUTPUT,
@Name varchar(250)
AS
UPDATE Keys
SET @RetVal = Key_Next = Key_Next + 1
FROM Keys
WHERE Key_Table = @Name
您需要在更新字段的同时设置变量的值。
答案 2 :(得分:0)
函数中的冷合并并发错误:
如果将具有ColdFusion getNextId函数的组件缓存在内存中,则可能会引起问题,因为非显式范围变量(例如RetVal变量)的默认作用域是组件级别,而不是函数一个不熟悉CF的人可能会认为,这使其对于组件是全局的。在函数中的参数之后添加以下行,以使其不再容易受到并发问题的影响(将变量定义为函数的局部变量。)
<cfset var RetVal />
或者,只要在变量出现在函数中时,都可以在变量前加上local.
作为前缀,以明确地将变量作用于函数。例如:local.RetVal
。 (适用于CF9 +)