【问题标题】:What is the simple way to find the column name from Lineageid in SSIS在SSIS中从Lineageid中查找列名的简单方法是什么
【发布时间】:2011-11-14 11:03:17
【问题描述】:

在 SSIS 中从 Lineageid 查找列名的简单方法是什么。有没有可用的系统变量?

【问题讨论】:

标签: ssis


【解决方案1】:

我记得说过这并不难,我可以在错误重定向中编写一些脚本来从输入集合中查找列名。

string badColumn = this.ComponentMetaData.InputCollection[Row.ErrorColumn].Name;

我了解到,失败的列不在该集合中。好吧,但是报告的 ErrorColumn 并不是我所需要的。我找不到那个包,但这里有一个例子说明为什么我不能得到我需要的东西。希望你能有更好的运气。

这是一个简单的数据流,一旦由于被零除而命中派生列,就会产生错误。 Derived 列生成一个新的输出列 (LookAtMe) 作为除法的结果。错误输出上的数据查看器告诉我失败的列是 73。使用上面的脚本逻辑,如果我尝试访问输入集合中的第 73 列,它将失败,因为它不在集合中。 LineageID 73 是 LookAtMe 并且 LookAtMe 不在我的错误分支中,它只在非错误分支中。

这是我的 XML 的副本,您可以看到,是的,outputColumn id 73 是 LookAtme。

<outputColumn id="73" name="LookAtMe" description="" lineageId="73" precision="0" scale="0" length="0" dataType="i4" codePage="0" sortKeyPosition="0" comparisonFlags="0" specialFlags="0" errorOrTruncationOperation="Computation" errorRowDisposition="RedirectRow" truncationRowDisposition="RedirectRow" externalMetadataColumnId="0" mappedColumnId="0"><properties>

但我真的很想要这些数据,而且我很聪明,所以我可以将所有结果合并在一起,然后有条件地将其拆分出来以得到它。问题是,Union All is an asynchronous transformation。异步转换导致数据从一组黄油复制到另一组黄油,从而导致...分配新的沿袭 ID,因此即使使用联合将两个流重新组合在一起,您也无法调用数据流链来查找原始谱系 ID,因为它位于不同的缓冲区中。

在这一点上,我承认失败并决定我可以在我的包中没有智能/有用的错误报告。

【讨论】:

    【解决方案2】:

    我知道这是一个很长的死线程,但我偶然发现了这个问题的手动解决方案,并认为我会与遇到同样问题的任何人分享。当然,这并没有为问题提供编程解决方案,但对于简单的调试,它应该可以解决问题。该解决方案使用派生列作为示例,但这似乎适用于任何数据流组件。

    答案由 Todd McDermid 提供,取自 AskSQLServerCentral

    "[...] 不幸的是,您的列的沿袭 ID 很好地隐藏在 SSIS 中。它是 SSIS 用来识别列的“键”。因此,为了确定它是哪一列,您需要打开派生列组件或数据转换的高级编辑器。通过右键单击并选择“高级编辑器”来执行此操作。转到“输入和输出属性”选项卡。打开第一个节点 - “派生列输入”或“数据”转换输入”。打开“输入列”选项卡。单击列,注意每个列的“LineageID”属性。您可能必须对“派生列输出”节点和其中的“输出列”执行相同操作。与您记录的沿袭 ID 匹配的列是违规列。”

    【讨论】:

      【解决方案3】:

      对于使用 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 的博客文章中提取信息并在此处重新创建它们来进行总结。 (如果你读过这篇文章,谢谢你们!)

      来自 Todd 的页面:

      1. 进入“输入和输出”页面,选择“输出0”节点。 将“SynchronousInputID”属性更改为“无”。 (这改变 脚本从同步到异步。)
        1. 在同一页面上,打开“Output 0”节点并选择“Output 列”文件夹。按“添加列”按钮。更改“名称” 此新列的属性为“LineageID”。
        2. 再次按“添加列”按钮,并更改“数据类型” 属性为“Unicode 字符串 [DT_WSTR]”,并更改“名称” 属性为“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. 它正在获取与输入相关的虚拟输入。 “输入”是 脚本实际上可以在输入中“看到”什么 - 因为我们 没有将任何列标记为“ReadOnly”或“ReadWrite”...... 表示输入没有列。然而,“虚拟输入”有 存在的每一列的完整列表,无论我们是否已经 说我们正在“使用”它。
        2. 然后我们循环遍历此虚拟机上的所有“虚拟列” 输入,并且对于每一个...
        3. 获取 LineageID 和列名,并将它们作为新行推出 我们的异步脚本。

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

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

      剩下的需要解释的部分是条件分割 – 这只是为了向脚本组件提供元数据 创建地图。我创建了一个表达式 (1 == 0),它总是 对于“No Rows – Metadata Only”路径,计算结果为 false,因此没有行 永远旅行。

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

      【讨论】:

        【解决方案4】:

        没有简单的方法可以通过 lineage id 找出列名。

        如果您想使用 BIDS 了解这一点,您必须使用高级属性、输入和输出列选项卡检查数据流中的所有组件,并查看每个列的 LineageID 和输入/输出路径。

        但你可以:

        但是,第二个解决方案包括大量编码和对管道的理解,因为您必须以编程方式:打开包、迭代任务、迭代容器内、迭代数据流内的转换以找到特定组件以使用建议的方法。

        【讨论】:

        • 这在 SSIS2012(SSDT 下)中是极难实现的。我尝试了上面和这里提到的所有方法 - 我的运气为零,其他一些(更有经验的)程序员也是如此
        • 这在 SSIS 2012 或更高版本中不起作用,因为 LineageID 现在是在运行时生成的。
        【解决方案5】:

        这是一个解决方案:

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

        Check out the full solution here.

        编辑

        这是简短的版本。

        1. 创建 2 个对象变量,execsObjlineageIds
        2. 在控制流中创建脚本任务,为其授予对两个变量的读写访问权限
        3. 为 Microsoft.SqlServer.DTSPipelineWrap.dll 的脚本任务添加程序集引用(可能需要安装 SQL 客户端 SDK;下面的 MainPipe 对象需要)
        4. 将以下代码插入到您的脚本任务中
        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);
            }
        }
        
        1. 在 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);
          }
        

        【讨论】:

        • 请不要只发布其他 Stack Overflow 问题的链接答案。相反,请投票/标记以将此问题作为重复问题结束,或者,如果不是重复问题,请调整您对此特定问题的答案。
        • 这种方法是我发现在 SSIS 2012 和 2014 中的脚本任务中工作的唯一方法。在 SSIS 2016 中,您在 IDTSComponentMetaData130 中有一个新函数,GetIdentificationStringByID,可以使用从 this.ComponentMetaData 中获取名称。
        猜你喜欢
        • 1970-01-01
        • 2019-04-26
        • 1970-01-01
        • 1970-01-01
        • 2013-01-26
        • 1970-01-01
        • 2017-12-25
        • 2017-11-20
        • 1970-01-01
        相关资源
        最近更新 更多