34

我正在将行从平面文件源重定向到平面文件目标。重定向行中的默认元数据是:

  • 原始平面文件源行
  • 错误代码
  • 错误列

我在输出平面文件中得到的是源行(很好)和错误代码(不好,例如-1071628249)和错误列(不好,因为它是列的内部 ID)。

如何转换行以输出错误消息(例如“数据被截断。”)和平面文件源中定义的列名?

换句话说,...,-1071607675,10我不想看到:

...,The data was truncated,Firstname

或者(如果以前不可能);

...,DTS_E_FLATFILESOURCEADAPTERSTATIC_TRUNCATED,Firstname.

4

10 回答 10

19

错误消息列表位于以下位置:MSDN、Integration Services 错误和消息参考 https://docs.microsoft.com/en-us/sql/integration-services/integration-services-error-and-message-reference?view =sql-server-ver15

并且列Id Number可以在SSIS的Data Flow Task中找到:选择产生错误的任务组件,Advanced Editor,'Input and Output Properties'选项卡,External Columns Properties。

于 2014-01-13T03:14:59.573 回答
15

部分问题(添加错误描述)可以通过脚本组件来实现。这在使用脚本组件增强错误输出中进行了描述。

看来Dougbert博客有添加列名的解决方案,但远非简单。我很惊讶这在 SSIS 中很难做到。你会认为知道源和列的名称是基本需要。

于 2012-10-03T08:59:48.660 回答
6

有一个更简单的答案。只需将错误输出重定向到新的目标文件(CSV 或其他文件),然后在错误输出上启用 DataViewer....

SSIS 中的数据查看器

于 2016-09-02T07:09:36.303 回答
6

它可以使用脚本组件作为转换来实现,将错误输出重定向到脚本组件并按照步骤来实现您要查找的内容。

(1) 打开脚本组件,

输入列选择

  1. 错误列
  2. 错误代码

输入和输出添加输出列

  1. 错误描述 (DT_STRING 500)
  2. 错误列描述 (DT_STRING 100)

(2) 编辑脚本

粘贴以下代码

    using System;
    using System.Data;
    using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
    using Microsoft.SqlServer.Dts.Runtime.Wrapper;
    #endregion

    /// <summary>
    /// This is the class to which to add your code.  Do not change the name, attributes, or parent
    /// of this class.
    /// </summary>
    [Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
    public class ScriptMain : UserComponent
    {

        public override void Input0_ProcessInputRow(Input0Buffer Row)
        {
            var component130 = this.ComponentMetaData as IDTSComponentMetaData130;
            if (component130 != null)
            {
                Row.ErrorDescription = component130.GetErrorDescription(Row.ErrorCode);
                Row.ErrorColumnDescription = component130.GetIdentificationStringByID(Row.ErrorColumn);
            }
        }
于 2017-07-25T23:56:18.483 回答
5

Pragmatic Works 似乎有一个错误输出描述转换,它是他们称之为“任务工厂”的产品社区版(免费)的一部分。

错误输出描述转换为用户提供了一个用户界面,可以检索有价值的信息,例如 ErrorCode、ErrorColumn、ErrorDescription、ComponentName(产生错误)、ColumnName(如果知道)、ColumnType 和 ColumnLength。

它还允许您将任何输入列传递到错误输出。老实说,它非常方便,并为我节省了数小时的 SSIS 包故障排除时间。

于 2013-06-03T19:37:15.973 回答
2
    //column error description 
    Row.ErrorDescription = this.ComponentMetaData.GetErrorDescription(Row.ErrorCode);

    //we are getting column name with some extra information
    String rawColumnName = this.ComponentMetaData.GetIdentificationStringByLineageID(Row.ErrorColumn);

    //extracting positions of brackets 
    int bracketPos = rawColumnName.LastIndexOf('[')+1;
    int lastBracketPos = rawColumnName.LastIndexOf(']');

    //extracting column name from the raw column name
    Row.ErrorColName = rawColumnName.Substring(bracketPos, (lastBracketPos - bracketPos));
于 2016-02-29T13:14:29.157 回答
2

使用SS2016及以上,很简单: https ://www.mssqltips.com/sqlservertip/4066/retrieve-the-column-causing-an-error-in-sql-server-integration-services/

public override void Input0_ProcessInputRow(Input0Buffer Row)
{
    Row.ErrorDescription = this.ComponentMetaData.GetErrorDescription(Row.ErrorCode);
    IDTSComponentMetaData130 componentMetaData = this.ComponentMetaData as IDTSComponentMetaData130;
    Row.ErrorColumnName = componentMetaData.GetIdentificationStringByID(Row.ErrorColumn);
}

对于在 SS2016 之前使用 SQL Server 版本的任何人,这里有几个参考链接,用于获取列名称:

http://www.andrewleesmith.co.uk/2017/02/24/finding-the-column-name-of-an-ssis-error-output-error-column-id/

这是基于: http ://toddmcdermid.blogspot.com/2016/04/finding-column-name-for-errorcolumn.html

我很感激我们不应该只发布链接,但是这个解决方案非常复杂,我试图通过从 Todd 和 Andrew 的博客文章中提取信息并在此处重新创建它们来进行总结。(如果你读过这篇文章,谢谢你们!)

从托德的页面:

  1. 转到“输入和输出”页面,然后选择“输出 0”节点。将“SynchronousInputID”属性更改为“无”。(这会将脚本从同步更改为异步。)
    1. 在同一页面上,打开“Output 0”节点并选择“Output Columns”文件夹。按“添加列”按钮。将此新列的“名称”属性更改为“LineageID”。
    2. 再次按下“添加列”按钮,将“DataType”属性更改为“Unicode string [DT_WSTR]”,将“Name”属性更改为“ColumnName”。
    3. 转到“脚本”页面,然后按“编辑脚本”按钮。将此代码复制并粘贴到 ScriptMain 类中(您可以删除所有其他方法存根):
public override void CreateNewOutputRows() {
    IDTSInput100 input = this.ComponentMetaData.InputCollection[0];
    if (input != null)
    {
        IDTSVirtualInput100 vInput = input.GetVirtualInput();
        if (vInput != null)
        {
            foreach (IDTSVirtualInputColumn100 vInputColumn in vInput.VirtualInputColumnCollection)
            {
                Output0Buffer.AddRow();
                Output0Buffer.LineageID = vInputColumn.LineageID;
                Output0Buffer.ColumnName = vInputColumn.Name;
            }
        }
    } }

随意使用数据查看器将虚拟输出附加到该脚本,看看你得到了什么。从这里开始,它是您 ETL 专家的“标准工程”。只需将失败组件的错误输出与此元数据合并,您就可以将 ErrorColumn 编号转换为有意义的列名。

但是对于那些确实想了解上述脚本在做什么的人:

  1. 它正在获取附加到脚本组件的“第一个”(也是唯一的)输入。
    1. 它正在获取与输入相关的虚拟输入。“输入”是脚本实际上可以在输入上“看到”的内容 - 由于我们没有将任何列标记为“只读”或“读写”......这意味着输入没有列。但是,“虚拟输入”具有存在的每一列的完整列表,无论我们是否说过我们正在“使用”它。
    2. 然后我们遍历这个虚拟输入上的所有“虚拟列”,并且对于每一个......
    3. 获取 LineageID 和列名,并将它们作为我们异步脚本的新行推出。

Andrew 页面中的图像和文本有助于更详细地解释它:

在此处输入图像描述

然后将此映射与沿错误路径向下的 ErrorColumn 沿袭 ID 合并连接,以便错误信息可以附加到映射中的列名称。我包含了第二个脚本组件,它从错误代码中查找错误描述,因此我们在上面看到的错误表行包含列名和错误描述。

需要解释的剩余组件是条件拆分——这只是为了向创建地图的脚本组件提供元数据。我创建了一个表达式 (1 == 0),对于“No Rows – Metadata Only”路径,它的计算结果始终为 false,因此没有行会沿着它向下移动。

虽然此解决方案确实需要在数据流中插入一些额外的管道,但当确实发生错误时,我们会记录非常有价值的信息。因此,特别是当数据流在生产环境中无人看管地运行时——当我们在设计时没有可用的工具和技术来找出问题所在时——结果日志记录为我们提供了关于问题所在和原因的更精确的信息,与简单地给我们失败的数据并让我们弄清楚它被拒绝的原因相比。

于 2018-10-09T12:53:04.460 回答
1

这是一个解决方案

  1. 在包运行时工作(不预填充)
  2. 通过脚本任务和组件自动化
  3. 不涉及安装新程序集或自定义组件
  4. 很好地兼容 BIML

在此处查看完整的解决方案。


这是简短的版本。

  1. 创建 2 个对象变量,execsObj以及lineageIds
  2. 在控制流中创建脚本任务,为其提供对两个变量的读写访问权限
  3. 将以下代码插入您的脚本任务
Dictionary<int, string> lineageIds = null;

public void Main()
{
    // 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);

    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);
    }
}

4. 在 Dataflow 中创建具有只读访问权限lineageIds和以下代码的脚本组件。

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.ErrorColumnName = lineageIds[colNum.Value];

          else
              Row.ErrorColumnName = "Row error";
      }
      Row.ErrorDescription = this.ComponentMetaData.GetErrorDescription(Row.ErrorCode);
  }
于 2015-10-31T02:30:03.090 回答
1

我使用数据选项卡上的从网络获取数据,使用 Excel 连接到 SSIS 错误消息参考网页。将表格保存在 Excel 中的工作表中,然后将其导入 SQL Server。然后将它加入到十进制代码上的错误行表中以获取描述,然后从中创建一个视图。认为这对于那些不想弄乱脚本任务的人可能很有用。

于 2018-06-25T21:38:58.080 回答
1

这两天一直在拉头发。我做了到处提到的所有事情,但是 package/c# 抛出了一个错误。最后,当我决定放弃时,我发现我的 ErrorColumn 出现为 0(零),因为由于违反 PK/FK 约束,错误出现在整行中。

所以我修改了脚本如下:

public override void Input0_ProcessInputRow(Input0Buffer Row)
    {
        Row.ErrorDescription = this.ComponentMetaData.GetErrorDescription(Row.ErrorCode);

        var componentMetaData130 = this.ComponentMetaData as IDTSComponentMetaData130;
        if (componentMetaData130 != null)
        {
            if (Row.ErrorColumn == 0) //Checking if the Column is zero
            {
                Row.ColumnName = "Entire Row. Check PK FK constraints"; //Hardcoded error message
            }
            else
            {
                Row.ColumnName = componentMetaData130.GetIdentificationStringByID(Row.ErrorColumn);
            }
        }
    }

对于通常的过程:https ://docs.microsoft.com/en-us/sql/integration-services/extending-packages-scripting-data-flow-script-component-examples/enhancing-an-error-output-with- the-script-component?view=sql-server-2017

为什么 ErrorColumn 值为零?:SSIS 2016 - ErrorColumn 为 0(零)

希望有帮助!!!

于 2019-01-02T11:09:59.973 回答