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

时间:2012-03-05 20:46:32

标签: sql vb.net ssis etl

提前感谢您的帮助。我需要帮助编写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               Item Name
    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           Item Name
1            Apple01
1            02
1            Banana01
1            02
1            03
2            Spoon1
2            2
2            Fork1
2            2
2            3
2            4

1 个答案:

答案 0 :(得分:8)

如果我在这个回答中表现得很迂腐,那不是我的意图。根据评论“我是新编码和故障排除问题”,我想了解我的观察以及我是如何找到它们的。

问题分析

希望根据与行关联的分隔字段将单行拆分为多个输出行。

现在的代码是生成适当的行数,因此您可以使脚本的异步部分(拆分)正常工作,这是一个加号。需要做的是我们需要1)填充子ID列2)在生成子项时将项前缀应用于所有后续行。

我对待这样的大多数问题。我想要完成什么?什么工作?什么不起作用?需要做些什么才能使其发挥作用。将问题分解为越来越小的问题最终会导致您可以做的事情。

代码观察

在提供的代码中粘贴导致错误,即未声明itemList。根据用法,似乎它的目的是itemName。

修复之后,您应该注意到IDE指示您有2个未使用的变量(posID,childID),并且variable txHolder is used before it's been assigned a value. A null reference exception could result at runtime.我的同事经常评论警告是尚未成长的错误,所以我给您的建议作为一个初出茅庐的开发人员,要注意警告,除非你明确指望编译器警告你所说的情况。

入门

在解决Child ID情况与名称前缀/后缀之间做出选择时,我会先从一个简单的问题开始,即孩子ID

生成代理键

这是一个花哨的标题短语,如果你搜索过你,你会对ssistalk或sqlis或任何一些非常聪明的博主提供大量的点击。魔鬼当然知道要搜索什么。没有你在哪里计算或将子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,则它是后缀,但您如何知道 base 是什么?第一行使用前导0并且是两位数长,而第二行不使用前导零。这是您需要了解数据的地方。识别第一行“代码”部分的规则是什么?一些可能的算法

  • 强制您的上游数据提供商提供一致的长度代码(我认为这在我的13年中曾经发挥过一次,但是对于反击来源却没有伤害)
  • 假设代码始终为数字,请按相反顺序评估每个字符是否可以转换为整数(处理可变长度代码)
  • 假设split数组中的第二个元素将提供代码的长度。这是您使用代码的方法,它实际上是有效的。

除了修复局部变量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