使用保持外键关系的SSIS加载多个表

我试图使用SSIS将数据从单个文件(具有百万条记录)加载到SQL Server上的多个表中,同时保持文件中定义的关系。

为了更好地阐述一个例子,我们假设我正在尝试加载一个文件,其中包含员工姓名,他们在过去占据的办公地点以及他们的职位标题历史记录,并由选项卡分隔。

文件:

EmployeeName<tab>OfficeHistory<tab>JobLevelHistory
John Smith<tab>501<tab>Engineer
John Smith<tab>601<tab>Senior Engineer
John Smith<tab>701<tab>Manager
Alex Button<tab>601<tab>Senior Assistant
Alex Button<tab>454<tab>Manager

如果我的Office数据库架构具有下列表格:

Employee (nId, name)
Office (nId, number)
JobTitle (nId, titleName)
Employee2Office (nEmpID, nOfficeId)
Employee2JobTitle (nEmpId, nJobTitleID)

我如何使用SSIS将文件加载到上面的架构中,为Employee,Office和JobTitle自动生成ID并维护员工和办公室之间以及员工和职位的关系?

所以在这种情况下。 表格应如下所示:

Employee
1 John Smith
2 Alex Button

Office
1 501
2 601
3 701
4 454

JobTitle
1 Engineer
2 Senior Engineer
3 Manager
4 Senior Assistant

Employee2Office
1 1
1 2
1 3
2 2
2 4

Employee2JobTitle
1 1
1 2
1 3
2 4
2 3

我是SSIS的新手,并没有在执行数据流任务时使用自动生成的ID和建立外键关系。 任何指针将不胜感激。

谢谢!


一个有趣的问题。 这里是我将如何做(Sql Server 2005)。 (我假设这是一个月度工作,而不只是一次,所以我添加了可重复性代码。)

  • 为Employee,JobTitle和Office表创建三个变量(type = Object)
  • 使用三个sql任务将这三个表的行选择到相应的变量中。
  • 添加数据流任务。
  • 使用平面文件目标从您的平面文件中选择。
  • 输出进入脚本组件,其中包含平面文件中的三列作为输入,将三个表变量导入到脚本中,脚本组件中的五个输出分别具有相同的排除组编号和标记为同步输出的输入七个添加到输出中的新列(emp为每个输出3个,其中2个为工作,2个为办公室),并使用以下代码(必须添加System.xml.dll引用才能完成此操作工作。):

    Imports System
    Imports System.Data
    Imports System.Math
    Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
    Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
    Imports System.Collections
    Imports System.Data.OleDb
    
    Public Class ScriptMain
        Inherits UserComponent
    
        Private da As New OleDbDataAdapter
        Private emp As New DataTable
        Private emph As New Hashtable()
        Private job As New DataTable
        Private jobh As New Hashtable()
        Private off As New DataTable
        Private offh As New Hashtable()
        Private maxempid As Integer
        Private maxjobid As Integer
        Private maxoffid As Integer
    
        Public Overrides Sub PreExecute()
            maxempid = 0
            maxjobid = 0
            maxoffid = 0
            da.Fill(emp, Me.Variables.EmpTab)
            For Each dr As DataRow In emp.Rows
                emph.Add(dr.Item("Name"), dr.Item("nID"))
                If (CInt(dr.Item("nID").ToString) > maxempid) Then
                    maxempid = CInt(dr.Item("nID").ToString)
                End If
            Next
            da.Fill(job, Me.Variables.JobTab)
            For Each dr As DataRow In job.Rows
                jobh.Add(dr.Item("titleName"), dr.Item("nID"))
                If (CInt(dr.Item("nID").ToString) > maxempid) Then
                    maxjobid = CInt(dr.Item("nID").ToString)
                End If
            Next
            da.Fill(off, Me.Variables.OffTab)
            For Each dr As DataRow In off.Rows
                offh.Add(dr.Item("number"), dr.Item("nID"))
                If (CInt(dr.Item("nID").ToString) > maxempid) Then
                    maxoffid = CInt(dr.Item("nID").ToString)
                End If
            Next
            emp.Dispose()
            job.Dispose()
            off.Dispose()
            da.Dispose()
            MyBase.PreExecute()
        End Sub
    
    Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
    
        If Not emph.ContainsKey(Row.EmployeeName) Then
            maxempid += 1
            emph.Add(Row.EmployeeName, maxempid)
            Row.EmpId = maxempid
            Row.Emp2Id = maxempid
            Row.Emp3Id = maxempid
            Row.DirectRowToEmployee()
        Else
            Row.EmpId = CInt(emph.Item(Row.EmployeeName).ToString)
            Row.Emp2Id = CInt(emph.Item(Row.EmployeeName).ToString)
            Row.Emp3Id = CInt(emph.Item(Row.EmployeeName).ToString)
        End If
        If Not jobh.ContainsKey(Row.JobLevelHistory) Then
            maxjobid += 1
            jobh.Add(Row.JobLevelHistory, maxjobid)
            Row.JobId = maxjobid
            Row.Job2Id = maxjobid
            Row.DirectRowToJobTitle()
        Else
            Row.JobId = CInt(jobh.Item(Row.JobLevelHistory).ToString)
            Row.Job2Id = CInt(jobh.Item(Row.JobLevelHistory).ToString)
        End If
        If Not offh.ContainsKey(Row.OfficeHistory) Then
            maxoffid += 1
            offh.Add(Row.OfficeHistory, maxoffid)
            Row.OffId = maxoffid
            Row.Off2Id = maxoffid
            Row.DirectRowToOfficeNumber()
        Else
            Row.OffId = CInt(offh.Item(Row.OfficeHistory).ToString)
            Row.Off2Id = CInt(offh.Item(Row.OfficeHistory).ToString)
        End If
        Row.DirectRowToEmp2Job()
        Row.DirectRowToEmp2Off()
    End Sub        
    End Class
    
  • 这个脚本的结果(该脚本为输入数据中的新值生成id,它通过将现有表加载到脚本的预先执行部分中的哈希表中,然后通过检查名称的存在并根据该名称增加maxid并将其添加到散列,如果它添加到散列,它还将该行添加到适当的(emp,job或off)输出,或从每个行的散列中检索maxid)。上面的状态将被写入剩下的两个输出(emp2job和emp2off)。

  • 接下来,发送数据流进行查找(检查目标表中的现有行,然后检查目标oledb连接器(emp,job和off选中标识插入框,emp2job和emp2off取消选中检查约束)。

  • 如果您确定您希望加载的数据的引用完整性正常,则可以在脚本任务中禁用外键约束,然后在并行数据加载的情况下执行数据流,并在数据加载完成后再次启用约束。 如果数据不对,操作将失败。 你将不得不设计回滚或清理策略。

    另一种选择是以串行方式加载数据,从主表开始并在子表上完成。 我认为这是'更安全'的选择,因为它不会将您的数据完整性暴露给可能在ETL加载时使用这些表的其他用户。 我更喜欢这个选项。


    这是如何 - 仅用文字解释有点困难,但我会试试看:

    使用标识列在数据库中定义员工,办公室和职位表,以便自动生成ID。

    定义没有多对多表(不需要或不需要标识)

    在您的SSIS数据流中,您必须在几次传递中完成此操作,以便首先在数据库中建立ID,然后返回并插入多对多行。

    制作一个数据流:

  • 把一个数据源读入文件中
  • 将其分成三部分,并加入多重播放。 一个将用于员工,一个办公室,一个头衔。
  • 对于每一个,把一个排序(这通常是一个禁忌,但由于来源是文本,而不是数据库,我们必须去那里)。 将排序设置为仅通过三个字段中的一个,然后检查排序中的选项以删除重复项。 这为每个基表创建一个唯一列表(如选择不同)。
  • 为每个表连接一个目标。
  • 在第一个数据流之后,添加第二个数据流。 这将填充多对多关系行

  • 用数据源读取文件
  • 添加一个在数据库中查找Employee Name的查找并返回Employee ID。 这会为您提供上面生成的员工ID。 (这通常被称为通过业务或自然键来查找代理键)
  • 添加一个在数据库中找到标题的查找并返回标题ID
  • 为数据库中的Office添加查找,并返回Office ID
  • 再次将结果复制到两个副本中,一个用于员工办公室,另一个用于员工职位
  • 根据您需要的逻辑,可能再次使用Sort对这些进行重复数据删除(取决于您如何从输入中规范化的细节)
  • 将结果放入具有两个目标的多对多表中。
  • 链接地址: http://www.djcxy.com/p/44559.html

    上一篇: Loading Multiple Tables using SSIS keeping foreign key relationships

    下一篇: Good reference of color