重新运行存储过程 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 查询在代码中创建数据集。