SSIS - 脚本组件,将单个行拆分为多个行(父子变体)

5

非常感谢您的帮助。我需要针对编写SSIS脚本组件以将单行分隔为多行寻求帮助。以下是我查看的许多有用博客和文章:

http://beyondrelational.com/ask/public/questions/1324/ssis-script-component-split-single-row-to-multiple-rows-parent-child-variation.aspx

http://bi-polar23.blogspot.com/2008/06/splitting-delimited-column-in-ssis.html

然而,我需要一些编码方面的额外帮助才能完成该项目。基本上这是我想做的事情。
输入数据
ID 项名称 1 Apple01,02,Banana01,02,03 2 Spoon1,2,Fork1,2,3,4
输出数据
ParentID ChildID Item Name 1 1 Apple01 1 2 Apple02 1 3 Banana01 1 4 Banana02 1 5 Banana03 2 1 Spoon1 2 2 Spoon2 2 3 Fork1 2 4 Fork2 2 5 Fork3 2 6 Fork4
以下是我的编码尝试,但如果不合逻辑,请随意修改。SSIS异步输出已设置。
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
    Dim posID As Integer, childID As Integer
    Dim delimiter As String = ","
    Dim txtHolder As String, suffixHolder As String
    Dim itemName As String = Row.ItemName
    Dim keyField As Integer = Row.ID

    If Not (String.IsNullOrEmpty(itemList)) Then

        Dim inputListArray() As String = _
        itemList.Split(New String() {delimiter}, _
        StringSplitOptions.RemoveEmptyEntries)

        For Each item As String In inputListArray
            Output0Buffer.AddRow()
            Output0Buffer.ParentID = keyField

            If item.Length >= 3 Then
                txtHolder = Trim(item)
                Output0Buffer.ItemName = txtHolder

                'when item length is less than 3, it's suffix
            Else
                suffixHolder = Trim(item)
                txtHolder = Left(txtHolder.ToString(), Len(txtHolder) _
                    - Len(suffixHolder)) & suffixHolder.ToString()
                Output0Buffer.ItemName = txtHolder
            End If
        Next
    End If
End Sub

当前代码产生以下输出

ID           项目名称
1            苹果01
1            02
1            香蕉01
1            02
1            03
2            匙1
2            2
2            叉1
2            2
2            3
2            4


1
那么,你遇到了什么错误? - Bill
我在编程中遇到了逻辑问题。它没有按照我想要的方式解析行。 - ElegantFellow
请根据上述输入更新问题,说明当前的代码产生了什么结果。 - billinkc
当前代码使用逗号分隔符进行解析。但是它存在以下问题。1)它不会连接前缀和后缀,例如我得到Apple01,02在不同的行中。它应该输出Apple01,Apple02在不同的行中。2)不会生成父子ID编号。 - ElegantFellow
Bill,谢谢你的反馈。我是编程新手,遇到了疑难问题需要解决。 - ElegantFellow
将您的代码粘贴到脚本组件中会导致错误,提示itemList未声明。 - billinkc
1个回答

8
如果我的回复显得过于学究,那并不是我的本意。基于“我是编码新手,遇到了问题”的评论,我想逐步解释一下我的观察和思考过程。
问题分析
希望将单行数据根据行相关的分隔字段拆分为多个输出行。
目前的代码生成了适当数量的行,因此您已经成功实现了脚本的异步部分(拆分),这是一个好的开始。接下来需要做的是:1)填充子ID列;2)在生成子项时为所有后续行应用项目前缀。
我通常会像这样处理大部分问题。我要达成什么目标?哪些方面正常工作?哪些方面出现了问题?需要做什么才能让它正常工作?将问题分解成越来越小的问题最终会得到您可以解决的问题。
代码观察
粘贴提供的代码会导致错误,提示itemList未声明。根据使用情况,它似乎应该是itemName。
修复之后,您应该注意到IDE指示您有两个未使用的变量(posID、childID),以及variable txHolder is used before it's been assigned a value. A null reference exception could result at runtime.。我的同事经常说警告就是还没长大成为错误的错误,因此我的建议是,作为一个初学者的开发人员,要注意警告,除非您明确希望编译器警告您有关该场景的问题。
入门指南
在解决子ID情况与名称前缀/后缀问题之间进行选择时,我会从简单的问题开始,即子ID。
生成代理键
这是一个花哨的标题短语,如果您搜索它,您会得到很多ssistalk或sqlis等极其聪明的博客的结果。当然,重点是知道要搜索什么。您从未计算或分配子ID值到流中,这就是为什么它不显示的原因。
我们只需要生成一个单调递增的数字,并在源ID更改时重置它。我假设传入的ID在传入数据中是唯一的,例如销售发票号码是唯一的,而我们正在拆分购买的商品。但是,如果数据集中重复了这些ID,那么也许它们代表的不是发票号码,而是销售人员ID。销售员1可能还有另一行批量销售蔬菜。这是一个更复杂的情况,如果这更好地描述了您的源数据,我们可以再次讨论。
生成代理键有两个部分(再次将问题分解成更小的部分)。第一件事是制作一个从1到N的计数器。您已经定义了一个childId变量来提供此服务。初始化此变量(1),然后在foreach循环中将其递增。
现在我们要进行计数,需要将该值推送到输出流中。将这两个步骤结合起来的样子如下:
        childID = 1
        For Each item As String In inputListArray
            Output0Buffer.AddRow()
            Output0Buffer.ParentId = keyField
            Output0Buffer.ChildId = childID
            ' There might be VB shorthand for ++
            childID = childID + 1

运行包并成功!将生成的替代键从列表中划掉。 surrogate key generated

字符串拼接

我不知道另一半问题需要做些什么,但我需要为这个部分起个标题。鉴于源数据,这可能更难以正确获取。你提供了Apple01、Banana01、Spoon1、Fork1的值。看起来有一个模式(名称与代码串联),但是它是什么?你的代码表明如果小于3,则是后缀,但是你如何知道基础是什么?第一行使用前导0且长度为两位数,而第二行则没有使用前导零。这就是你需要了解数据的地方。如何确定第一行的“代码”部分的规则?一些可能的算法:

  • 强制上游数据提供者提供一致长度的代码(在我13年的工作经验中,我认为这只起过一次作用,但反击源头从未伤害过)
  • 假设代码始终为数字,倒序评估拆分数组中的每个字符,测试它是否可以转换为整数(处理可变长度代码)
  • 假设拆分数组中的第二个元素将提供代码的长度。这是你在代码中采取的方法,它实际上是有效的。

我没有对生成的项目名称进行任何更改,除了修复局部变量ItemName/itemList。最终代码通过删除PosID并将txtHolder初始化为空字符串来消除警告。

Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
    Dim childID As Integer
    Dim delimiter As String = ","
    Dim txtHolder As String = String.Empty, suffixHolder As String
    Dim itemName As String = Row.ItemName
    Dim keyField As Integer = Row.ID

    If Not (String.IsNullOrEmpty(itemName)) Then

        Dim inputListArray() As String = _
        itemName.Split(New String() {delimiter}, _
        StringSplitOptions.RemoveEmptyEntries)

        ' The inputListArray (our split out field)
        ' needs to generate values from 1 to N
        childID = 1
        For Each item As String In inputListArray
            Output0Buffer.AddRow()
            Output0Buffer.ParentId = keyField
            Output0Buffer.ChildId = childID
            ' There might be VB shorthand for ++
            childID = childID + 1

            If item.Length >= 3 Then
                txtHolder = Trim(item)
                Output0Buffer.ItemName = txtHolder
            Else
                'when item length is less than 3, it's suffix
                suffixHolder = Trim(item)
                txtHolder = Left(txtHolder.ToString(), Len(txtHolder) _
                    - Len(suffixHolder)) & suffixHolder.ToString()
                Output0Buffer.ItemName = txtHolder
            End If
        Next
    End If
End Sub

亲爱的比尔,这一点也不啰嗦。我有时为了解决编译错误而感到有些沮丧。由于某种原因或者是我的无知,BIDS不允许我运行调试器以观察变量。我现在正在使用“On Error Resume Next"来过渡。非常感谢您在此方面的友善帮助和时间,这对我在系统地找到解决方法方面帮助很大。对于字符串拼接,我的输入数据相当“杂乱”,并且我没有结构化规则来处理基本和后缀。除了基础似乎是在字母数字后缀之前出现之外,其中大多数小于或等于3位数。 - ElegantFellow
我无法强制数据长度保持一致。看起来我不能过滤掉所有数据的特殊性,只能以广泛的模式清理它们以备将来使用。一旦数据被清理干净,我计划使用模糊查找(如Levenshtein或Jaccard指数)来引用这些数据。再次感谢您的帮助。代理键运作得非常好。 - ElegantFellow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接