为什么我的SSIS包在首次运行时失败但在第二次运行时失败? “无法将对象从DBNULL转换为其他类型”

问题描述 投票:2回答:1

我有一个问题导致我的SSIS包在第一次执行时失败,而在第二次执行时问题自动解决并且SSIS包成功执行。

项目:迁移包

  • SQL Server 2008 R2到SQL Server,SSDT 2016
  • Visual Studio 2008到Visual Studio 2015

案件:

我的一个迁移的SSIS包,其脚本任务包括:

  • 使用一个OUTPUT参数执行存储过程以获取行计数
  • 将返回的行以CSV格式写入txt文件。

存储过程在此脚本任务下执行;返回(数千,数百万行)以及一个OUTPUT参数,该参数包含存储过程返回的行数。

问题:

这个SSIS包在第一次执行时随机失败,错误 -

“无法将对象从DBNULL转换为其他类型”

并在第二次执行成功执行。

当我们谷歌这个错误时,最常见的解决方案是首先检查变量值:它是否为INT值,并且具有任何值,然后仅转换。

但是,此解决方案不适用于此,因为当我第二次执行SSIS包时,它会成功执行。

此处需要注意的另一件事是,当SSIS包随机失败时,它会将CSV文件写入不完整的行,而在第二次执行时,此问题也会得到解决。

看起来上面的错误不是实际错误,并且由于未知原因没有给出实际错误。

是由于SSIS / SQL级别或任何其他问题上缺少任何配置?

请在下面找到SCRIPT和存储过程代码。

每个随机故障都在SCRIPT代码的下面。但是,在第二次运行中没有出现这样的问题。所以,看起来它不是转换问题,而是更多的朝向2016配置或一些不同的编码方式 -

  listRowCounts.Add(Convert.ToInt32(outputparm.Value));

请在下面找到:

  1. 脚本任务代码部分,错误即将到来
  2. 存储过程代码部分,通过它我们导出行计数并在OUTPUT变量中设置它

在代码下面的脚本任务中,创建了多个日志记录点,以了解代码运行的时间点 -

// Write to sysout
Console.WriteLine("Test<NUMBER>");

--||--**-- --||--**-- --||--**-- --||--**-- --||--**-- 
 SCRIPT code part for WriteData only--

        public void WriteData(string strExamRegionCode, string strdivisionCode, string strResultFileName, DateTime dtmStdDate)
         {
             SqlConnection conn = new SqlConnection(strConnectionStringLog);
             SqlCommand cmd = null;
             SqlDataReader rdr = null;

            try
             {
                 //Open the connection 
                 conn.Open();

                // Create the command 
                 cmd = new SqlCommand("usp_Exam_ResultManifest_ProcList_Get", conn);
                 cmd.CommandType = CommandType.StoredProcedure;
                 cmd.Parameters.Add(new SqlParameter("@Exam_region_code", strExamRegionCode));
                 cmd.Parameters.Add(new SqlParameter("@Exam_division_code", strdivisionCode));
                 cmd.Parameters.Add(new SqlParameter("@Result_file_name", strResultFileName));
                 // Return the resultset 
                 rdr = cmd.ExecuteReader();


                 // Write to sysout
                 Console.WriteLine("Test12");

                // Fail if no rows returned
                 if (!rdr.HasRows)
                 {
                     // Log error to database 
                     string strCustomMessage = "No data returned by calling stored procedure usp_Exam_ResultManifest_ProcList_Get with parameters: " + strExamRegionCode + ", " + strdivisionCode + ", " + strResultFileName + ", " + dtmStdDate.ToString("yyyyMMdd");
                     LogCustomMessage(strConnectionStringLog, "OnError", strMachineName, strUserName, strPackageName, strPackageID, strExecutiondivisionGUID, strContainerStartTime, 100, strCustomMessage);

                    // Write to sysout
                     Console.WriteLine(strCustomMessage);

                    // Fail the package - error will be written to table sysssislog 
                     throw new MyAppException(strCustomMessage);

                }
                 else
                 {
                     // Call the Result Get stored procedure(s) to retrieve the Result data

                    while (rdr.Read())
                     {
                         // Get the stored procedure name
                         string strResultGetStoredProcedureName = rdr["Result_get_storedprocedure_name"].ToString();

                        // Write to sysout
                         Console.WriteLine("Test13");

                        try
                         {
                             SqlConnection connFDA = new SqlConnection(strConnectionStringResult);
                             SqlCommand cmdFDA = null;
                             SqlDataReader rdrFDA = null;

                            //Open the connection 
                             connFDA.Open();

                            // Run the sproc to return the Result data
                             cmdFDA = new SqlCommand(strResultGetStoredProcedureName, connFDA);
                             cmdFDA.CommandType = CommandType.Text;

                            SqlParameter parm1 = new SqlParameter("@Std_date", SqlDbType.DateTime);
                             parm1.Value = dtmStdDate;
                             parm1.Direction = ParameterDirection.Input;
                             cmdFDA.Parameters.Add(parm1);

                            SqlParameter parm2 = new SqlParameter("@Exam_Source_Section_division", SqlDbType.VarChar);
                             parm2.Value = strdivisionCode;
                             parm2.Direction = ParameterDirection.Input;
                             cmdFDA.Parameters.Add(parm2);

                            SqlParameter outputparm = new SqlParameter("@rows_returned", SqlDbType.Int);
                             outputparm.Direction = ParameterDirection.Output;
                             outputparm.Size = int.MaxValue;
                             cmdFDA.Parameters.Add(outputparm);

                            // Write to sysout
                             Console.WriteLine("Test14");

                            if (Dts.Variables["strForceRecompileObjects"].Value.ToString().Contains(strResultGetStoredProcedureName))
                             {

                                cmdFDA.CommandText = cmdFDA.CommandText + " @Std_date, @Exam_Source_Section_division, @rows_returned OUT WITH RECOMPILE;";

                                // Write to sysout
                                 Console.WriteLine("Test15");

                            }
                             else
                             {
                                 cmdFDA.CommandText = cmdFDA.CommandText + " @Std_date, @Exam_Source_Section_division, @rows_returned OUT;";

                                // Write to sysout
                                 Console.WriteLine("Test16");

                            }

                            // Result file generation timeout issue
                             cmdFDA.CommandTimeout = 1600;

                            // Write to sysout
                             Console.WriteLine("B4_cmdFDA_Execution");

                            // Return the resultset 
                             rdrFDA = cmdFDA.ExecuteReader();

                            // Write to sysout
                             Console.WriteLine("AFTER_cmdFDA_Execution");

                            if (rdrFDA.HasRows)
                             {
                                 // Write to sysout
                                 Console.WriteLine("rdrFDA has rows.");
                             }
                             else
                             {
                                 // Write to sysout
                                 Console.WriteLine("rdrFDA has NO rows.");

                            }


                            // Write to sysout
                             Console.WriteLine("TT");

                            // Write to sysout
                             Console.WriteLine("Test17");

                            try
                             {
                                 // Loop through the Result data and write to the file
                                 while (rdrFDA.Read())
                                 {

                                    // Write the row data to the file
                                     string strRowData = rdrFDA["row_data"].ToString();

                                    // Write to sysout
                                     //Console.WriteLine("Test18");

                                    int intControlId = Convert.ToInt32(rdrFDA["Result_control_id"]);
                                     if (!listControlIds.Contains(intControlId))
                                     {
                                         listControlIds.Add(intControlId);
                                         // Write to sysout
                                         Console.WriteLine("Test19");
                                     }

                                    WriteFile(strRowData);
                                     // Write to sysout
                                     //Console.WriteLine("Test20");

                                }

                            }
                             catch (Exception ex)
                             {
                                 // Log error to database 
                                 string strCustomMessage = "Error rdrFDA.Read  - stored procedure " + strResultGetStoredProcedureName + " with parameters: " + dtmStdDate.ToString("yyyyMMdd") + ", " + strdivisionCode + ", Error: " + ex.Message;
                                 LogCustomMessage(strConnectionStringLog, "OnError", strMachineName, strUserName, strPackageName, strPackageID, strExecutiondivisionGUID, strContainerStartTime, 100, strCustomMessage);

                                // Write to sysout
                                 Console.WriteLine(strCustomMessage);

                                // Fail the package - error will be written to table sysssislog 
                                 throw;
                             }

                            // Close the reader
                             rdrFDA.Close();

                            // Write to sysout
                             Console.WriteLine("Test21");


                             // Write to sysout
                             Console.WriteLine("abc");
                             Console.WriteLine("xyz" + Convert.ToString(outputparm.Value));
                             Console.WriteLine("def");

                            // Keep track of row counts - for the trailer row (MUST be after closing the reader)
                             listRowCounts.Add(Convert.ToInt32(outputparm.Value));

                            // Write to sysout
                             Console.WriteLine("Test22");

                            // Close the connection
                             connFDA.Close();
                         }
                         catch (Exception ex)
                         {
                             // Log error to database 
                             string strCustomMessage = "Error retrieving data for writing to the output file  - stored procedure " + strResultGetStoredProcedureName + " with parameters: " + dtmStdDate.ToString("yyyyMMdd") + ", " + strdivisionCode + ", Error: " + ex.Message;
                             LogCustomMessage(strConnectionStringLog, "OnError", strMachineName, strUserName, strPackageName, strPackageID, strExecutiondivisionGUID, strContainerStartTime, 100, strCustomMessage);

                            // Write to sysout
                             Console.WriteLine(strCustomMessage);

                            // Fail the package - error will be written to table sysssislog 
                             throw;
                         }
                     }

                    rdr.Close();
                     conn.Close();

                }
             }
             catch (Exception ex)
             {
                 // Log error to database 
                 string strCustomMessage = "Error retrieving data for writing to the output file with parameters: " + strExamRegionCode + ", " + strdivisionCode + ", " + strResultFileName + ", " + dtmStdDate.ToString("yyyyMMdd") + ", Error: " + ex.Message;
                 LogCustomMessage(strConnectionStringLog, "OnError", strMachineName, strUserName, strPackageName, strPackageID, strExecutiondivisionGUID, strContainerStartTime, 100, strCustomMessage);

                // Write to sysout
                 Console.WriteLine(strCustomMessage);

                // Fail the package - error will be written to table sysssislog 
                 throw;
             }
         }


 --||--**-- --||--**-- --||--**-- --||--**-- --||--**-- 


 --||--**-- --||--**-- --||--**-- --||--**-- --||--**-- 
 ---Stored proedure code where setting OUTPUT parameter value ONLY: - 

CREATE PROCEDURE [dbo].[p_result_get_SchoolItems_exam_v18]       
    @std_date DATETIME = NULL,
    @exam_Source_Section_division VARCHAR(10) = NULL,
    @rows_returned INT OUTPUT,
    @debug TINYINT = 0
WITH EXECUTE AS CALLER
AS
    SET NOCOUNT ON

    /*
     ** Declare and set error tracking and debugging variables
     */

    DECLARE @ProcName            sysname,
            @Error               int,
            @Raiserror           int,
            @CustomErrorSeverity int ,
            @CustomErrorState    int,
            @ErrorSeverity       int ,
            @ErrorState          int,
            @Msg                 varchar(255),
            @Rowcount            int, 
            @RowCnt              int;

     SET @ProcName = object_name(@@procid);
     SET @Error = 0;
     SET @Raiserror = 0;
     SET @Msg = '';
     SET @Rowcount = 0;
     SET @RowCnt = 0;
     SET @CustomErrorSeverity = 11;
     SET @CustomErrorState = 1;

    /*
     ** Declare variables used to implement procedure specific functionality
     */
     DECLARE @default_date datetime;
     DECLARE @working_date datetime;
     DECLARE @exam_region_code varchar(10);  
     DECLARE @SchoolID varchar(8);
     DECLARE @result_control_id int;
     SELECT @default_date = '29991231';


     BEGIN TRY

        IF (@debug>=1) PRINT @ProcName + ' : ' + convert(varchar(30),getdate(),109) + ': Entering procedure ...';

            --To avoid NULL/DBNULL issues coming in SSIS Package execution due to below direct SET via @@ROWCOUNT; added same Query as above but with COUNT(1) only. 
             --SET @rows_returned = @@ROWCOUNT;

            SELECT @RowCnt = COUNT(1) 
             FROM    dbo.t_result_SchoolItems_exam result
             JOIN    dbo.t_result_VerificationList_exam con
             ON      result.result_control_id = con.result_control_id
             AND     con.exam_division_code = result.exam_division_code
             JOIN    dbo.t_result_name_exam n
             ON      con.result_name_id = n.result_name_id
             JOIN    dbo.t_result_Active_Verification_id_exam curr
             ON      con.result_control_id = curr.result_control_id
             AND     curr.exam_division_code = result.exam_division_code
             WHERE   n.result_name = 'PatternD book'
             AND     con.exam_region_code = @exam_region_code
             AND     con.exam_bus_date = @std_date
             AND     result.exam_division_code = @exam_Source_Section_division


             --ORDER BY result.system_id, result.Roll_ID, result.Ce_value_local_ledgerK, result.due_local_ledgerK, result.cash_amount_local_ledgerK
             OPTION (RECOMPILE);

            SET @rows_returned = @RowCnt; 


     END TRY


     BEGIN CATCH
         SELECT  @Raiserror = 300000 + error_number() ,
                 @ErrorSeverity = error_severity() ,
                 @ErrorState = error_state() ,
                 @Msg = @ProcName + ': ' + isnull ( error_message() , @Msg ) + ' , Error Number = ' + isnull ( convert ( varchar , error_number()) , '' )
                          + ' , Error Line = ' + isnull ( convert ( varchar , error_line()) , 'N/A' );

         RAISERROR (@Msg, @ErrorSeverity, @ErrorState);
          RETURN @Raiserror;
     END CATCH;


GO

--||--**-- --||--**-- --||--**-- --||--**-- --||--**--
sql-server visual-studio ssis sql-server-data-tools
1个回答
3
投票

在没有看到脚本任务和存储过程的代码的情况下,很难具体地诊断出错了什么,但我会采取一些裂缝。

在脚本任务中,您正在运行存储过程,它返回结果集以及输出参数。某处,您有来自存储过程的NULL。数据库“stuff”中的NULL被转换为.NET框架内的复杂类型。我写不出来

int i = null;

因为我的变量i是原始类型。它必须具有合理的价值。我可以写

int? i = null;

因为?表明这是一个可以为空的类型。 SQL语言没有这种复杂性,因此很高兴为变量赋值null。

DECLARE @i int = NULL;

在“脚本任务”中,您需要检查尝试访问DataRow / DataTable / DataSet对象的所有位置以及输出参数的Parameters.Value属性。

为什么它在第一次之后有效?

我不能说。也许存储过程内置了逻辑以“获取自上次执行以来的所有数据”,并且新运行在某些字段中没有NULL。这也可以解释文件部分填充的原因。如果你在访问它时弄清楚数据库中的那行是什么,你可能会在那里找到一个NULL。

参考阅读

© www.soinside.com 2019 - 2024. All rights reserved.