重新运行存储过程 returns 之前的结果集
Rerunning stored procedure returns previous resultset
使用 Visual Studio 2017 和 SQL 服务器 2019。
我正在编写一个应用程序来计算花费的总废物成本。要计算这个,我需要每 material 每公斤的废物成本。此费用每年都会发生变化。
这些浪费成本存储在数据库中。当应用程序加载时,这些成本由存储过程收集。这是一个动态结果集。它加载最近 x 年的数据(其中 x 由用户确定)。这个存储过程以开始年份作为输入参数(smallint)并收集从那一年开始的所有数据。这意味着我有一个结果集,它可以有不同数量的列,具有不同的列名。
如果我收集最近 5 年的数据。我会得到 table 返回列 .
用户可以添加年份。去年的成本然后复制到新的一年(这发生在数据库中)。之后,应用程序将再次从数据库中加载最近 x 年的数据。这就是问题所在。
我重新运行收集过去x年浪费成本的存储过程。应用程序给出正确的参数值(开始年份),数据库返回正确的数据。 (在存储过程中,我将输入和输出写成单独的 table 用于调试,所以我确信这一点!)我在 C# 中看到的返回数据 table 保存了我之前的列从数据库中收集。
我的代码:
public DataTable SpDataTableOutput(string spName, SpParameter[] param)
{
SqlConnection db = new SqlConnection(this.connectionString);
SqlCommand cmd = new SqlCommand(spName, db);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter adapter = new SqlDataAdapter();
DataTable resultSet = new DataTable();
try
{
// Add parameters
this.AddParameters(param, cmd);
this.AddReturnValue(cmd);
// Get the results
db.Open();
adapter.SelectCommand = cmd;
adapter.Fill(resultSet);
db.Close();
// Get the return value
int returnValue = GetReturnValue(cmd);
// If the sp fails, throw an exception
if (returnValue != 0)
throw new Exception("The database returned a return value of " + Convert.ToString(returnValue));
}
catch (Exception ex)
{
throw new Exception("Failed to run sp '" + spName + "'", ex);
}
finally
{
adapter.Dispose();
adapter = null;
cmd.Dispose();
cmd = null;
db.Dispose();
db = null;
}
return resultSet;
}
问题不在存储过程中。就是在上面的方法中。调试它,我看到为数据库提供了正确的参数,在数据库中我看到提供了正确的输入参数并返回了正确的结果集。当我在adapter.Fill(resultSet);
中查看返回数据table时,我看到了之前的数据集
例如,起始列为:<[=51=]|2017|2018|2019|2020|2021>.
如果我添加一年,我会期望 (这是数据库 returns),但我得到 .
如果我再增加一年,我会期望 ,但仍然会得到 .
我假设这是一个缓存问题,因为它并不总是发生(只是大多数时候)。
此外,如果我更改我希望显示的年数(不刷新数据)并添加一年,数据将始终正确刷新(这确实意味着存储过程 returns 不同的列数)。
最后,我看到这种情况发生在我添加一年后,关闭应用程序并重新启动它(全部处于调试模式)。即使数据在数据库中,初始浪费成本负载也不会显示新年。
我希望这是清楚的。
如何在每次运行上述方法时得到一个新的数据集?
存储过程 - 这已经过测试并且工作完美
ALTER PROCEDURE [dbo].[sp_GetWasteCost_v01]
-- Parameters for the stored procedure
@ipStartYear smallint
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @varTimeStamp datetime
SET @varTimeStamp = CURRENT_TIMESTAMP
-- DEBUG --
INSERT INTO z_InputParam (
InsertTime,
StartYear)
VALUES (
@varTimeStamp,
@ipStartYear)
-- DEBUG --
-- Generate the resultset
DECLARE @varMaxYear smallint
SELECT @varMaxYear = MAX([Year])
FROM dbo.WasteCosts
CREATE TABLE #tblResults (
MaterialID int,
Material varchar(100))
-- Add a column for each year
DECLARE @varCnt int
SET @varCnt = @ipStartYear
DECLARE @varLiteral varchar(MAX)
WHILE (@varCnt <= @varMaxYear) BEGIN
SET @varLiteral = 'ALTER TABLE #tblResults ADD [' + CAST(@varLoopCnt AS varchar(4)) + '] decimal(10,4) NULL'
EXEC(@varLiteral)
SET @varCnt = @varCnt + 1
END
-- Fill the dataset
INSERT INTO #tblResults (
MaterialID,
Material)
SELECT DISTINCT
M.MaterialID,
M.CatNo,
M.[Description]
FROM dbo.tbl_Material M
INNER JOIN dbo.tbl_WasteCost WC
ON M.MaterialID = WC.MaterialID
WHERE WC.[Year] BETWEEN @ipStartYear AND @varMaxYear
-- Get the data for each year
SET @varCnt = @ipStartYear
WHILE (@varCnt <= @varMaxYear) BEGIN
SET @varLiteral =
'UPDATE RS ' +
'SET RS.[' + CAST(@varCnt AS varchar(4)) + '] = WC.WasteCost ' +
'FROM #tblResults RS ' +
'INNER JOIN dbo.WasteCost WC ' +
'ON RS.MaterialID = WC.MaterialID ' +
'WHERE WC.[Year] = ' + CAST(@varCnt AS varchar(4))
EXEC(@varLiteral)
SET @varCnt = @varCnt + 1
END
-- DEBUG --
INSERT INTO z_Output (
InsertTime,
ColumnName)
SELECT
@varTimeStamp,
CAST(C.[name] AS varchar(100))
FROM tempdb.sys.columns C
WHERE object_id = Object_id('tempdb..#tblResults');
-- DEBUG --
-- Return the resultset
SELECT
RS.*
FROM #tblResults RS
ORDER BY CatNo
-- Cleanup
DROP TABLE #tblResultSet
END
参数代码 - 这有效并且在过去 10 年左右的时间里经过了全面测试。
private void AddParameters(SpParameter[] param, SqlCommand cmd)
{
foreach (SpParameter parameter in param)
{
cmd.Parameters.Add(parameter.GetParameter());
}
}
private void AddReturnValue(SqlCommand cmd)
{
SqlParameter returnValue = new SqlParameter("@return_value", SqlDbType.Int);
returnValue.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(returnValue);
}
public SqlParameter GetParameter()
{
// Create the return paramter
SqlParameter param = new SqlParameter(this.Name, this.Type);
// Set direction & nullable
param.Direction = this.Direction;
param.IsNullable = this.IsNullable;
// Set value
switch (this.Type)
{
case (SqlDbType.BigInt):
case (SqlDbType.Int):
case (SqlDbType.SmallInt):
case (SqlDbType.TinyInt):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToInt64(this.Value);
break;
case (SqlDbType.Bit):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToBoolean(this.Value);
break;
case (SqlDbType.Decimal):
case (SqlDbType.Float):
case (SqlDbType.Real):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToDouble(this.Value);
break;
case (SqlDbType.Char):
case (SqlDbType.NChar):
case (SqlDbType.VarChar):
case (SqlDbType.NVarChar):
case (SqlDbType.Xml):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = this.Value;
break;
case (SqlDbType.VarBinary):
if (this.VarBinaryValue == null)
param.Value = DBNull.Value;
else
param.Value = this.VarBinaryValue;
break;
case (SqlDbType.DateTime):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToDateTime(this.Value);
break;
}
// Return the parameter
return param;
}
我不知道究竟是什么导致了这个问题。随着时间的推移,情况会变得更糟。即使在我重新启动机器后问题仍然存在。所以这不是代码中的错误。肯定是其他原因造成的。
最后我去掉了存储过程。我使用 select 查询在代码中创建数据集。
使用 Visual Studio 2017 和 SQL 服务器 2019。
我正在编写一个应用程序来计算花费的总废物成本。要计算这个,我需要每 material 每公斤的废物成本。此费用每年都会发生变化。
这些浪费成本存储在数据库中。当应用程序加载时,这些成本由存储过程收集。这是一个动态结果集。它加载最近 x 年的数据(其中 x 由用户确定)。这个存储过程以开始年份作为输入参数(smallint)并收集从那一年开始的所有数据。这意味着我有一个结果集,它可以有不同数量的列,具有不同的列名。
如果我收集最近 5 年的数据。我会得到 table 返回列
用户可以添加年份。去年的成本然后复制到新的一年(这发生在数据库中)。之后,应用程序将再次从数据库中加载最近 x 年的数据。这就是问题所在。
我重新运行收集过去x年浪费成本的存储过程。应用程序给出正确的参数值(开始年份),数据库返回正确的数据。 (在存储过程中,我将输入和输出写成单独的 table 用于调试,所以我确信这一点!)我在 C# 中看到的返回数据 table 保存了我之前的列从数据库中收集。
我的代码:
public DataTable SpDataTableOutput(string spName, SpParameter[] param)
{
SqlConnection db = new SqlConnection(this.connectionString);
SqlCommand cmd = new SqlCommand(spName, db);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter adapter = new SqlDataAdapter();
DataTable resultSet = new DataTable();
try
{
// Add parameters
this.AddParameters(param, cmd);
this.AddReturnValue(cmd);
// Get the results
db.Open();
adapter.SelectCommand = cmd;
adapter.Fill(resultSet);
db.Close();
// Get the return value
int returnValue = GetReturnValue(cmd);
// If the sp fails, throw an exception
if (returnValue != 0)
throw new Exception("The database returned a return value of " + Convert.ToString(returnValue));
}
catch (Exception ex)
{
throw new Exception("Failed to run sp '" + spName + "'", ex);
}
finally
{
adapter.Dispose();
adapter = null;
cmd.Dispose();
cmd = null;
db.Dispose();
db = null;
}
return resultSet;
}
问题不在存储过程中。就是在上面的方法中。调试它,我看到为数据库提供了正确的参数,在数据库中我看到提供了正确的输入参数并返回了正确的结果集。当我在adapter.Fill(resultSet);
中查看返回数据table时,我看到了之前的数据集
例如,起始列为:<[=51=]|2017|2018|2019|2020|2021>.
如果我添加一年,我会期望
如果我再增加一年,我会期望
我假设这是一个缓存问题,因为它并不总是发生(只是大多数时候)。 此外,如果我更改我希望显示的年数(不刷新数据)并添加一年,数据将始终正确刷新(这确实意味着存储过程 returns 不同的列数)。
最后,我看到这种情况发生在我添加一年后,关闭应用程序并重新启动它(全部处于调试模式)。即使数据在数据库中,初始浪费成本负载也不会显示新年。
我希望这是清楚的。
如何在每次运行上述方法时得到一个新的数据集?
存储过程 - 这已经过测试并且工作完美
ALTER PROCEDURE [dbo].[sp_GetWasteCost_v01]
-- Parameters for the stored procedure
@ipStartYear smallint
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @varTimeStamp datetime
SET @varTimeStamp = CURRENT_TIMESTAMP
-- DEBUG --
INSERT INTO z_InputParam (
InsertTime,
StartYear)
VALUES (
@varTimeStamp,
@ipStartYear)
-- DEBUG --
-- Generate the resultset
DECLARE @varMaxYear smallint
SELECT @varMaxYear = MAX([Year])
FROM dbo.WasteCosts
CREATE TABLE #tblResults (
MaterialID int,
Material varchar(100))
-- Add a column for each year
DECLARE @varCnt int
SET @varCnt = @ipStartYear
DECLARE @varLiteral varchar(MAX)
WHILE (@varCnt <= @varMaxYear) BEGIN
SET @varLiteral = 'ALTER TABLE #tblResults ADD [' + CAST(@varLoopCnt AS varchar(4)) + '] decimal(10,4) NULL'
EXEC(@varLiteral)
SET @varCnt = @varCnt + 1
END
-- Fill the dataset
INSERT INTO #tblResults (
MaterialID,
Material)
SELECT DISTINCT
M.MaterialID,
M.CatNo,
M.[Description]
FROM dbo.tbl_Material M
INNER JOIN dbo.tbl_WasteCost WC
ON M.MaterialID = WC.MaterialID
WHERE WC.[Year] BETWEEN @ipStartYear AND @varMaxYear
-- Get the data for each year
SET @varCnt = @ipStartYear
WHILE (@varCnt <= @varMaxYear) BEGIN
SET @varLiteral =
'UPDATE RS ' +
'SET RS.[' + CAST(@varCnt AS varchar(4)) + '] = WC.WasteCost ' +
'FROM #tblResults RS ' +
'INNER JOIN dbo.WasteCost WC ' +
'ON RS.MaterialID = WC.MaterialID ' +
'WHERE WC.[Year] = ' + CAST(@varCnt AS varchar(4))
EXEC(@varLiteral)
SET @varCnt = @varCnt + 1
END
-- DEBUG --
INSERT INTO z_Output (
InsertTime,
ColumnName)
SELECT
@varTimeStamp,
CAST(C.[name] AS varchar(100))
FROM tempdb.sys.columns C
WHERE object_id = Object_id('tempdb..#tblResults');
-- DEBUG --
-- Return the resultset
SELECT
RS.*
FROM #tblResults RS
ORDER BY CatNo
-- Cleanup
DROP TABLE #tblResultSet
END
参数代码 - 这有效并且在过去 10 年左右的时间里经过了全面测试。
private void AddParameters(SpParameter[] param, SqlCommand cmd)
{
foreach (SpParameter parameter in param)
{
cmd.Parameters.Add(parameter.GetParameter());
}
}
private void AddReturnValue(SqlCommand cmd)
{
SqlParameter returnValue = new SqlParameter("@return_value", SqlDbType.Int);
returnValue.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(returnValue);
}
public SqlParameter GetParameter()
{
// Create the return paramter
SqlParameter param = new SqlParameter(this.Name, this.Type);
// Set direction & nullable
param.Direction = this.Direction;
param.IsNullable = this.IsNullable;
// Set value
switch (this.Type)
{
case (SqlDbType.BigInt):
case (SqlDbType.Int):
case (SqlDbType.SmallInt):
case (SqlDbType.TinyInt):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToInt64(this.Value);
break;
case (SqlDbType.Bit):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToBoolean(this.Value);
break;
case (SqlDbType.Decimal):
case (SqlDbType.Float):
case (SqlDbType.Real):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToDouble(this.Value);
break;
case (SqlDbType.Char):
case (SqlDbType.NChar):
case (SqlDbType.VarChar):
case (SqlDbType.NVarChar):
case (SqlDbType.Xml):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = this.Value;
break;
case (SqlDbType.VarBinary):
if (this.VarBinaryValue == null)
param.Value = DBNull.Value;
else
param.Value = this.VarBinaryValue;
break;
case (SqlDbType.DateTime):
if (this.Value == null)
param.Value = DBNull.Value;
else
param.Value = Convert.ToDateTime(this.Value);
break;
}
// Return the parameter
return param;
}
我不知道究竟是什么导致了这个问题。随着时间的推移,情况会变得更糟。即使在我重新启动机器后问题仍然存在。所以这不是代码中的错误。肯定是其他原因造成的。
最后我去掉了存储过程。我使用 select 查询在代码中创建数据集。