SSIS Data Flow Error Handling With Description and Column Name
Without SQL 2016's modified API, it's really difficult to find the error column name using just a script task and script component. Here's a simple solution.
Join the DZone community and get the full member experience.
Join For FreeFinding an error description is a simple task involving one line of code — but finding the error column name is not so simple. Microsoft made it easy with SQL 2016's modified API. But with prior versions, it's a really difficult job to find the error column name using just a script task and script component.
This article provides a simple step-by-step solution to find the error description and error column name.
Scenario
We are trying to insert records from a flat file to our SQL table. The flat file has two columns: roll
and name
. Sometimes, the name
column contains huge text and isn't supported by our table object (the size of the name
column the in table object is Varchar(50)). This generates an error. We are trying to display the error description and the column name that generated the error.
Control/Data Flow Details
Control Flow: Get Lineage ID Task
This script task is used to grade the lineage ID at runtime and save it as a text file.
Create a text file:
C:\temp\write.txt
.Create two package-level variables named
User::execsObj
andUser::lineageIds
as the object types. Pass them as the Read-Write variables of the script task.In the script task, add an assembly named
Microsoft.SqlServer.DTSPipelineWrap
:using System; using System.Data; using Microsoft.SqlServer.Dts.Runtime; using Microsoft.SqlServer.Dts.Pipeline.Wrapper; using System.Windows.Forms; using System.Collections.Generic; using System.IO;
Script task code in C#:
{ // Grab the executables so we have to something to iterate over, and initialize our lineageIDs list // Why the executables? Well, SSIS won't let us store a reference to the Package itself... Dts.Variables["User::execsObj"].Value =(Package)Dts.Variables["User::execsObj"].Parent).Executables; Dts.Variables["User::lineageIds"].Value = new Dictionary<int, string>(); lineageIds = (Dictionary<int, string>)Dts.Variables["User::lineageIds"].Value; Executables execs = (Executables)Dts.Variables["User::execsObj"].Value; ReadExecutables(execs); // Just proof of concept to see the results before you dedicate your time to the solution // Delete this code in your actual implementation using (StreamWriter writetext = new StreamWriter(@"C:\temp\write.txt", true)) { foreach (var kvp in lineageIds) writetext.WriteLine(kvp.Key + " : " + kvp.Value); } Dts.TaskResult = (int)ScriptResults.Success; } private void ReadExecutables(Executables executables) { foreach (Executable pkgExecutable in executables) { if (object.ReferenceEquals(pkgExecutable.GetType(), typeof(Microsoft.SqlServer.Dts.Runtime.TaskHost))) { TaskHost pkgExecTaskHost = (TaskHost)pkgExecutable; if (pkgExecTaskHost.CreationName.StartsWith("SSIS.Pipeline")) { ProcessDataFlowTask(pkgExecTaskHost); } } else if (object.ReferenceEquals(pkgExecutable.GetType(), typeof(Microsoft.SqlServer.Dts.Runtime.ForEachLoop))) { // Recurse into FELCs ReadExecutables(((ForEachLoop)pkgExecutable).Executables); } } } private void ProcessDataFlowTask(TaskHost currentDataFlowTask) { MainPipe currentDataFlow = (MainPipe)currentDataFlowTask.InnerObject; foreach (IDTSComponentMetaData100 currentComponent in currentDataFlow.ComponentMetaDataCollection) { // Get the inputs in the component. foreach (IDTSInput100 currentInput in currentComponent.InputCollection) foreach (IDTSInputColumn100 currentInputColumn in currentInput.InputColumnCollection) lineageIds.Add(currentInputColumn.ID, currentInputColumn.Name); // Get the outputs in the component. foreach (IDTSOutput100 currentOutput in currentComponent.OutputCollection) foreach (IDTSOutputColumn100 currentoutputColumn in currentOutput.OutputColumnCollection) lineageIds.Add(currentoutputColumn.ID, currentoutputColumn.Name); } }
Adding a Script Component to the Data Flow Task
Now, add a script component to the data flow task.
Pass the variable named
User::lineageIds
as the Read variable.Script the component input columns as
ErrorCode
andErrorColumn
.Script the component output column as
ErrorDescription
andErrorColumnDescription
with aDT_STR
data type.
Script task code in C#:using System; using System.Data; using Microsoft.SqlServer.Dts.Pipeline.Wrapper; using Microsoft.SqlServer.Dts.Runtime.Wrapper; using System.Collections.Generic;
public override void Input0_ProcessInputRow(Input0Buffer Row) { Dictionary<int, string> lineageIds = (Dictionary<int, string>)Variables.lineageIds; int? colNum = Row.ErrorColumn; if (colNum.HasValue && (lineageIds != null)) { if (lineageIds.ContainsKey(colNum.Value)) Row.ErrorColumnDescription = lineageIds[colNum.Value]; else Row.ErrorColumnDescription = "Row error"; } Row.ErrorDescription = this.ComponentMetaData.GetErrorDescription(Row.ErrorCode); }
I hope you found this article informative!
Published at DZone with permission of Joydeep Das, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments