我有一个问题导致我的SSIS包在第一次执行时失败,而在第二次执行时问题自动解决并且SSIS包成功执行。
项目:迁移包
案件:
我的一个迁移的SSIS包,其脚本任务包括:
存储过程在此脚本任务下执行;返回(数千,数百万行)以及一个OUTPUT参数,该参数包含存储过程返回的行数。
问题:
这个SSIS包在第一次执行时随机失败,错误 -
“无法将对象从DBNULL转换为其他类型”
并在第二次执行成功执行。
当我们谷歌这个错误时,最常见的解决方案是首先检查变量值:它是否为INT值,并且具有任何值,然后仅转换。
但是,此解决方案不适用于此,因为当我第二次执行SSIS包时,它会成功执行。
此处需要注意的另一件事是,当SSIS包随机失败时,它会将CSV文件写入不完整的行,而在第二次执行时,此问题也会得到解决。
看起来上面的错误不是实际错误,并且由于未知原因没有给出实际错误。
是由于SSIS / SQL级别或任何其他问题上缺少任何配置?
请在下面找到SCRIPT和存储过程代码。
每个随机故障都在SCRIPT代码的下面。但是,在第二次运行中没有出现这样的问题。所以,看起来它不是转换问题,而是更多的朝向2016配置或一些不同的编码方式 -
listRowCounts.Add(Convert.ToInt32(outputparm.Value));
请在下面找到:
在代码下面的脚本任务中,创建了多个日志记录点,以了解代码运行的时间点 -
// 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
--||--**-- --||--**-- --||--**-- --||--**-- --||--**--
在没有看到脚本任务和存储过程的代码的情况下,很难具体地诊断出错了什么,但我会采取一些裂缝。
在脚本任务中,您正在运行存储过程,它返回结果集以及输出参数。某处,您有来自存储过程的NULL。数据库“stuff”中的NULL被转换为.NET框架内的复杂类型。我写不出来
int i = null;
因为我的变量i是原始类型。它必须具有合理的价值。我可以写
int? i = null;
因为?
表明这是一个可以为空的类型。 SQL语言没有这种复杂性,因此很高兴为变量赋值null。
DECLARE @i int = NULL;
在“脚本任务”中,您需要检查尝试访问DataRow / DataTable / DataSet对象的所有位置以及输出参数的Parameters.Value属性。
我不能说。也许存储过程内置了逻辑以“获取自上次执行以来的所有数据”,并且新运行在某些字段中没有NULL。这也可以解释文件部分填充的原因。如果你在访问它时弄清楚数据库中的那行是什么,你可能会在那里找到一个NULL。