为什么我的SSIS软件包在第一次运行时失败,而在第二次运行时却失败? “无法将对象从DBNULL强制转换为其他类型”

时间:2019-02-15 18:18:34

标签: sql-server visual-studio ssis sql-server-data-tools

我遇到了一个问题,该问题导致我的SSIS程序包第一次执行失败,而在第二次开始执行时,该问题会自动解决,并且SSIS程序包成功执行。

项目:迁移程序包

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

案例:

我的一个迁移的SSIS程序包之一,它具有以下脚本任务:

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

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

问题:

此SSIS程序包在第一次执行时随机失败,并显示错误-

  

“对象不能从DBNULL强制转换为其他类型”

及其第二次执行成功执行。

当我们搜索此错误时,找到的最常见的解决方案是先检查Variable值:它是否是INT值,是否具有任何值,然后才进行转换。

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

这里要注意的另一件事是,当SSIS包随机失败时,它将写入不完整行的CSV文件,并且在第二次执行时也解决了此问题。

上述错误似乎不是实际错误,并且由于未知原因未提供实际错误。

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

请在下面找到“脚本和存储过程”代码。

每个随机故障都位于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

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

1 个答案:

答案 0 :(得分:3)

在没有看到脚本任务和存储过程代码的情况下,要明确诊断出什么问题是很困难的,但是我会做些破解。

在脚本任务中,您正在运行存储过程,并且该过程将返回结果集以及输出参数。在某个地方,您有一个来自存储过程的NULL。数据库“材料”中的NULL被转换为.NET框架中的复杂类型。我不会写

int i = null;

因为我的变量i是原始类型。它必须具有一个有意义的值。我会写

int? i = null;

因为?指示此类型为可空。 SQL语言没有这种复杂性,因此很高兴为变量分配null。

DECLARE @i int = NULL;

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

为什么在第一次之后会起作用?

我不能说。也许存储过程内置了逻辑以“获取自上次执行以来的所有数据”,并且新运行在某些字段中没有NULL。那也可以解释为什么文件被部分填充。如果您在访问数据库时弄清楚该行在数据库中的位置,则很可能会发现其中存在一个NULL。

参考阅读