2

I have an issue which is causing my SSIS package failure in its first execution, while on 2nd onward execution the issue is automatically resolved and the SSIS package is executed successfully.

Project: Migrating a package

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

Case:

One of my migrated SSIS packages which has a script task to:

  • Execute stored procedure with one OUTPUT parameter to get rows count
  • Write the returned rows to a txt file in CSV format.

The stored procedure get executed under this script tasks; returns (thousands, millions of rows) along with one OUTPUT parameter which contains rows count return by the stored procedure.

Problem:

This SSIS Package is failing randomly on 1st execution with error -

"Object cannot be cast from DBNULL to other types"

and on its 2nd execution is executed successfully.

When we Google this error, the most common solution found is to check the Variable value first: whether it's an INT value, and has any value, and then only converts.

However, this solution does not apply here, as when I execute the SSIS package a 2nd time, it gets executed successfully.

One more thing to note here is that when the SSIS Package randomly fails, it writes the CSV file with an incomplete row, and on 2nd execution this issue is also resolved.

It looks like the above error is not actual error and due to unknown reasons not giving actual error.

Is it due to any configuration missing on SSIS / SQL level or any other issue?

Please find the SCRIPT & stored procedure code below.

Every random failure caught at below line of SCRIPT code. However, on 2nd run no such issue appeared. So, looks like its not a conversion issue but more towards 2016 configuration or some different way of coding issue -

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

Please find below the:

  1. script task code part under which the error is coming &

  2. Stored procedure code part by which we are deriving the rows count and setting it in OUTPUT variable

In Script task below code, there are multiple logging points created to know till what point the code is running as-

// 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

--||--**-- --||--**-- --||--**-- --||--**-- --||--**--
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
bsethi24
  • 45
  • 1
  • 8
  • No matter what, I would always check the variable for a value before I converted it. – JMabee Feb 15 '19 at 19:13
  • I don't know what your variable type is, but have you tried giving it a default value before you start? – JMabee Feb 15 '19 at 19:22
  • Yes... Using Script task to write file as as the rows count on very higher side and in addition to that need to add customize header & footer to the file. Due to all these, used Script task to create file. – bsethi24 Mar 18 '19 at 06:35
  • Could anyone also please advice whether any change in VS 2015 and/or SQL Server 2016 R2 as compare to Visual Studio 2008 / SQL 2008 R2 for below script task functionality? -ExecuteReader -new SqlCommand -SqlDataReader As either ExecuteReader or Sql DataReader mostly the area where the problem is as 1)By using these only we are executing and reading the retrived data & 2)On adhoc basis;either the execution not getting completed or DataReader is unable to read the executed output results Please advice if anything need to reconfigured for these script task functionality – bsethi24 Mar 18 '19 at 06:35

1 Answers1

3

Without seeing the code for both the script task and the stored procedure, it's rather difficult to diagnose specifically what is wrong but I'll take some cracks as it.

In your script task, you are running a stored procedure and it is returning a resultset as well as an output parameter. Somewhere, you have a NULL coming from the stored procedure. A NULL in database "stuff" is translated to a complex type within the .NET framework. I can't write

int i = null;

because my variable, i, is a primitive type. It must have a value that makes sense. I could write

int? i = null;

because the ? indicates this a nullable type. There's no such complexity in the SQL language so it's happy to assign null to a variable.

DECLARE @i int = NULL;

In your Script Task, you need to check all the places you are attempting to access your DataRow/DataTable/DataSet object as well as the Parameters.Value property of the output parameter.

Why does it work after the first time?

I couldn't say. Perhaps the stored procedure has logic built into it to "get all the data since last execution" and the new run doesn't have NULLs in some field. That could also explain why the file is partially filled. It's likely that if you go figure out what that row was in the database at the time you accessed it, you'll find a NULL sitting there.

Reference reading

TylerH
  • 20,799
  • 66
  • 75
  • 101
billinkc
  • 59,250
  • 9
  • 102
  • 159
  • The code is added as above .... Please advice if anymore information is required..... – bsethi24 Mar 18 '19 at 06:31
  • Using Script task to write file as as the rows count on very higher side and in addition to that need to add customize header & footer to the file. Due to all these, used Script task to create file. – bsethi24 Mar 18 '19 at 06:32
  • Could anyone also please advice whether any change in VS 2015 and/or SQL Server 2016 R2 as compare to Visual Studio 2008 / SQL 2008 R2 for below script task functionality? -ExecuteReader -new SqlCommand -SqlDataReader As either ExecuteReader or Sql DataReader mostly the area where the problem is as 1)By using these only we are executing and reading the retrived data & 2)On adhoc basis;either the execution not getting completed or DataReader is unable to read the executed output results Please advice if anything need to reconfigured for these script task functionality – bsethi24 Mar 18 '19 at 06:33