在SSIS中从Lineageid查找列名的简单方法是什么。是否有可用的系统变量?
答案 0 :(得分:4)
我记得说这不是那么难,我可以在错误重定向中编写一些脚本来从输入集合中查找列名。
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,因为它在不同的缓冲区中。
在这一点上,我承认失败并决定我可以在我的包中没有智能/有用的错误报告。
答案 1 :(得分:1)
我知道这是一个漫长的死线,但我绊倒了这个问题的手动解决方案,并认为我会分享任何遇到同样问题的人。虽然这并没有提供问题的程序化解决方案,但对于简单的调试,它应该可以解决问题。该解决方案使用派生列作为示例,但这似乎适用于任何数据流组件。
由Todd McDermid提供的答案,摘自AskSQLServerCentral:
&#34; [...]不幸的是,您的列的血统ID在SSIS中隐藏得非常好。这是&#34;关键&#34; SSIS用于识别列。因此,为了确定它是哪一列,您需要打开派生列组件或数据转换的高级编辑器。通过右键单击并选择&#34; Advanced Editor&#34;来做到这一点。转到&#34;输入和输出属性&#34;标签。打开第一个节点 - &#34;派生列输入&#34;或&#34;数据转换输入&#34;。打开&#34;输入列&#34;标签。点击列,注意&#34; LineageID&#34;每个人的财产。您可能必须使用&#34;派生列输出&#34;节点和&#34;输出列&#34;在里面。与您记录的沿袭ID匹配的列是违规列。&#34;
答案 2 :(得分:0)
没有简单的方法可以通过血统id找出列名。
如果您想使用BIDS了解这一点您必须使用“高级”属性,“输入和输出列”选项卡检查数据流中的所有组件,并查看每列和输入/输出路径的LineageID。
但你可以:
然而,第二个解决方案包括很多编码和理解管道,因为你必须programmaticaly:打开包,迭代任务,迭代容器内部,迭代数据流内部的转换,找到特定组件使用提出的方法。 / p>
答案 3 :(得分:0)
以下是一个解决方案:
Check out the full solution here.
这是简短版本。
execsObj
和lineageIds
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);
}
}
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);
}
答案 4 :(得分:0)
对于使用SS2016之前版本的SQL Server的任何人,这里有一些参考链接,用于获取列名称:
基于: http://toddmcdermid.blogspot.com/2016/04/finding-column-name-for-errorcolumn.html
我很欣赏我们不应该发布链接,但是这种解决方案非常复杂,我试图通过从Todd和Andrew的博客文章中提取信息并在此处重新创建来进行总结。 (如果您都读过这篇文章,谢谢你们!)
在Todd的页面上:
- 转到“输入和输出”页面,然后选择“输出0”节点。 将“ SynchronousInputID”属性更改为“无”。 (此更改 脚本从同步到异步。)
- 在同一页面上,打开“输出0”节点,然后选择“输出” 列”文件夹。按“添加列”按钮。更改“名称” 该新列的“ LineageID”属性。
- 再次按“添加列”按钮,然后更改“数据类型” 属性设置为“ Unicode字符串[DT_WSTR]”,然后更改“名称” 属性为“ ColumnName”。
- 转到“脚本”页面,然后按“编辑脚本”按钮。复制 并将此代码粘贴到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号转换为有意义的列名。
但是对于那些想了解以上脚本内容的人 在做:
- 正在将“第一个(也是唯一)”输入附加到脚本 零件。
- 正在获取与输入有关的虚拟输入。 “输入”是 该脚本实际上可以在输入中“看到”什么-并且由于我们 没有将任何列标记为“ ReadOnly”或“ ReadWrite” ... 表示输入没有NO列。但是,“虚拟输入”具有 无论是否存在,每个存在的列的完整列表 说我们正在“使用”它。
- 然后,我们遍历此虚拟机上的所有“虚拟列” 输入,然后每一个...
- 获取LineageID和列名,并将其作为新行推出 我们的异步脚本。
安德鲁(Andrew)页面上的图片和文字有助于对其进行更详细的说明:
然后将此地图与ErrorColumn世系ID合并合并。 沿着错误路径前进,以便可以将错误信息 附加了地图中的列名。我包括了一秒钟 从错误中查找错误描述的脚本组件 代码,因此我们在上面看到的错误表行同时包含两列 名称和错误说明。
需要说明的其余部分是条件拆分 –存在的目的只是向脚本组件提供元数据 创建地图。我创建了一个表达式(1 == 0) 对于“无行–仅元数据”路径,结果为false,因此没有行 往下走。
尽管此解决方案确实需要插入一些其他内容 深入数据流中,我们获得了非常有价值的信息 确实发生错误时记录。所以特别是当数据流是 在生产中无人值守时运行–当我们没有工具和 设计时可用的技术来找出问题所在– 记录的结果为我们提供了有关以下信息的更精确信息 出了什么问题以及为什么,与仅向我们提供失败的数据相比 并让我们弄清楚为什么它被拒绝了。