存储过程只运行一次
Stored procedure only runs once
我有一个 SQL 服务器存储过程:
CREATE PROCEDURE dbo.GetNextNumber
@NextNumber int OUT
AS
UPDATE SystemSettings
SET REGISTRY_NUMBER = REGISTRY_NUMBER + 1
WHERE ID = 1;
SELECT @NextRegistryNumber = REGISTRY_NUMBER
FROM SystemSettings
WHERE ID = 1;
SELECT 'NextNumber' = @NextRegistryNumber;
我从 C# API 方法调用它:
public int GetNextRegistryNumberFC()
{
string procedure = "GetNextNumber";
// Create ADO.NET objects.
// SqlConnection con = new SqlConnection(connectionString);
// SqlCommand cmd = new SqlCommand(procedure, con);
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings[<ConnectionString>].ConnectionString))
using (var cmd = new SqlCommand(procedure, con))
try
{
// Configure command and add input parameters.
cmd.CommandType = CommandType.StoredProcedure;
// Add the output parameter.
cmd.Parameters.Add("@NextNumber", SqlDbType.Int).Direction = ParameterDirection.Output;
// Execute the command.
con.Open();
cmd.ExecuteNonQuery();
NextRegistryNumber = Convert.ToInt32(cmd.Parameters["@NextNumber"].Value);
}
catch (Exception ex)
{
throw;
}
finally
{
cmd.Dispose();
con.Close();
con.Dispose();
}
return NextRegistryNumber;
}
这是我第一次使用 运行 它。之后它每次只是 return 相同的数字而不是增加它。我不确定我发生了什么。如果我 运行 存储过程本身每次都有效。有人知道这里出了什么问题吗?
这是打字错误吗?
SELECT 'NextNumber' = @NextRegistryNumber;
这将输出一个 table,其中包含一个名为 'NextNumber' 的列,其值在 @NextRegistryNumber 中,即:您已经完成了一个列别名。
应该是:
SELECT @NextNumber = @NextRegistryNumber;
或
SET @NextNumber = @NextRegistryNumber;
如果两个或多个连接同时调用此存储过程,将产生意外结果。两次执行都会增加该字段,然后 return 相同的值。即使此问题已修复,更新查询也会在单行中创建一个热点,并发请求在等待彼此更新该行时阻塞。
为了避免这种情况,SQL 服务器(和其他数据库)有 SEQUENCE 可以使用最小锁定自动递增的对象:
CREATE SEQUENCE Registration_Numbers START WITH 1;
NEXT VALUE FOR 函数将增加一个序列和 return 新值:
SELECT NEXT VALUE FOR Registration_Numbers;
这个查询很短,不需要存储过程:
var sql="SELECT NEXT VALUE FOR Registration_Numbers;";
using(var con=new SqlConnection(connectionString))
using(var cmd=new SqlCommand(sql,con))
{
var new_id=(long)cmd.ExecuteScalar();
return new_id;
}
这里不需要try/catch
。 using
块确保连接和命令对象将被释放,即使发生异常也是如此。 using
即使在 finally
不会被调用的情况下也能正常工作。
也不需要重新抛出。下面的 catch
块与没有 catch
完全没有区别。
catch (Exception ex)
{
throw;
}
如果异常被处理(例如记录),这样的块将有意义。
原子更新和读取
如果使用 table 是绝对必要的(为什么?),OUTPUT 子句可用于 return 来自 UPDATE 命令的新值:
CREATE PROCEDURE dbo.GetNextNumber
AS
UPDATE SystemSettings
SET REGISTRY_NUMBER = REGISTRY_NUMBER + 1
OUTPUT inserted.REGISTRY_NUMBER
WHERE ID = 1;
可以使用相同的代码来调用存储过程。只有 CommandType
需要更改
var proc_name=" dbo.GetNextNumber";
using(var con=new SqlConnection(connectionString))
using(var cmd=new SqlCommand(proc_name,con))
{
cmd.CommandType = CommandType.StoredProcedure;
var new_id=(long)cmd.ExecuteScalar();
return new_id;
}
我有一个 SQL 服务器存储过程:
CREATE PROCEDURE dbo.GetNextNumber
@NextNumber int OUT
AS
UPDATE SystemSettings
SET REGISTRY_NUMBER = REGISTRY_NUMBER + 1
WHERE ID = 1;
SELECT @NextRegistryNumber = REGISTRY_NUMBER
FROM SystemSettings
WHERE ID = 1;
SELECT 'NextNumber' = @NextRegistryNumber;
我从 C# API 方法调用它:
public int GetNextRegistryNumberFC()
{
string procedure = "GetNextNumber";
// Create ADO.NET objects.
// SqlConnection con = new SqlConnection(connectionString);
// SqlCommand cmd = new SqlCommand(procedure, con);
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings[<ConnectionString>].ConnectionString))
using (var cmd = new SqlCommand(procedure, con))
try
{
// Configure command and add input parameters.
cmd.CommandType = CommandType.StoredProcedure;
// Add the output parameter.
cmd.Parameters.Add("@NextNumber", SqlDbType.Int).Direction = ParameterDirection.Output;
// Execute the command.
con.Open();
cmd.ExecuteNonQuery();
NextRegistryNumber = Convert.ToInt32(cmd.Parameters["@NextNumber"].Value);
}
catch (Exception ex)
{
throw;
}
finally
{
cmd.Dispose();
con.Close();
con.Dispose();
}
return NextRegistryNumber;
}
这是我第一次使用 运行 它。之后它每次只是 return 相同的数字而不是增加它。我不确定我发生了什么。如果我 运行 存储过程本身每次都有效。有人知道这里出了什么问题吗?
这是打字错误吗?
SELECT 'NextNumber' = @NextRegistryNumber;
这将输出一个 table,其中包含一个名为 'NextNumber' 的列,其值在 @NextRegistryNumber 中,即:您已经完成了一个列别名。
应该是:
SELECT @NextNumber = @NextRegistryNumber;
或
SET @NextNumber = @NextRegistryNumber;
如果两个或多个连接同时调用此存储过程,将产生意外结果。两次执行都会增加该字段,然后 return 相同的值。即使此问题已修复,更新查询也会在单行中创建一个热点,并发请求在等待彼此更新该行时阻塞。
为了避免这种情况,SQL 服务器(和其他数据库)有 SEQUENCE 可以使用最小锁定自动递增的对象:
CREATE SEQUENCE Registration_Numbers START WITH 1;
NEXT VALUE FOR 函数将增加一个序列和 return 新值:
SELECT NEXT VALUE FOR Registration_Numbers;
这个查询很短,不需要存储过程:
var sql="SELECT NEXT VALUE FOR Registration_Numbers;";
using(var con=new SqlConnection(connectionString))
using(var cmd=new SqlCommand(sql,con))
{
var new_id=(long)cmd.ExecuteScalar();
return new_id;
}
这里不需要try/catch
。 using
块确保连接和命令对象将被释放,即使发生异常也是如此。 using
即使在 finally
不会被调用的情况下也能正常工作。
也不需要重新抛出。下面的 catch
块与没有 catch
完全没有区别。
catch (Exception ex)
{
throw;
}
如果异常被处理(例如记录),这样的块将有意义。
原子更新和读取
如果使用 table 是绝对必要的(为什么?),OUTPUT 子句可用于 return 来自 UPDATE 命令的新值:
CREATE PROCEDURE dbo.GetNextNumber
AS
UPDATE SystemSettings
SET REGISTRY_NUMBER = REGISTRY_NUMBER + 1
OUTPUT inserted.REGISTRY_NUMBER
WHERE ID = 1;
可以使用相同的代码来调用存储过程。只有 CommandType
需要更改
var proc_name=" dbo.GetNextNumber";
using(var con=new SqlConnection(connectionString))
using(var cmd=new SqlCommand(proc_name,con))
{
cmd.CommandType = CommandType.StoredProcedure;
var new_id=(long)cmd.ExecuteScalar();
return new_id;
}