问题描述
我有一个带有索引唯一 ID 的 sql 数据库。 我正在使用 SSIS 将数据从 Excel 导入 sql 数据库。如果该 ID 存在于数据库中,当我的 sql Server 数据库中存在重复项时,我会收到错误无法在具有唯一索引的对象中插入重复的键行,我希望 SSIS 跳过导入该行,如果行(它的 ID)存在于数据库中,则跳转到下一个。我该如何解决这个问题?
解决方法
您可以使用:
- 使用条件拆分转换进行聚合转换
将聚合转换带到数据流窗格并将您的 Excel 文件源连接到聚合转换。您需要将它们分组并计数。
将条件拆分转换拖到数据流窗格并将聚合转换连接到它。
通过使用条件拆分,您会将重复记录重定向到输出,您可以将它们写入表/文件。
使用 Int
将 [Count All] 转换为 Cast Function (DT_I4)
。
条件拆分转换将创建两个输出:
-
DuplicateRecords
用于重复记录 -
CorrectRecords
用于唯一记录
带来一个 Multicast Transformation
并将 DuplicateRecords
的输出从 Conditional Split Transformation
连接到它。
带来一个 OLE DB 目标并将 CorrectRecords
的输出从 Conditional Split Transformation
连接到它。
在它们之间放置数据查看器,以便您可以显示记录。您将实时将这些记录写入目标表/文件。
2.查找组件:
在控制流中添加一个 Lookup task
并使用它加入 Excel 文件任务。
在 Lookup Transformation Editor
中,指定连接管理器和缓存类型。
在指定如何处理没有匹配条目的行的下拉菜单中,选择以下选项:
Redirect rows to no match output
单击连接并从下拉列表中指定目标表。您在同一数据库中同时拥有源表和目标表。您也可以在此处创建单独的连接。
单击列,它会显示源表和目标表。 您想比较 Excel 文件和目标表上的 ID 列。从源中拖动 ID 列并将其移动到目标 ID 列。
从 SSIS 工具箱添加 OLE DB destination
。使用 OLE DB destination
加入 Lookup 任务,它会打开以下输入-输出选择。
选择以下值:
- 输出:查找无匹配输出:您将其附加到
OLE DB destination
(SQL Server 数据库中的表) - 输入:OLE DB 目标输入:例如,您将其附加到平面文件目标
3. SSIS 排序结合脚本组件:
将您的源添加到数据流任务。
您需要对要用于重复数据删除的列进行排序。通常,如果您的源是数据库表,那么您应该在查询中添加一个 GROUP BY
子句,否则您应该在源之后添加一个 SORT Transformation
。
在排序的源之后添加脚本组件类型转换。
编辑 Script Component
并转到 Input Columns tab
并选择要用于重复数据删除的所有列作为 ReadOnly
(与用于排序的字段相同)。
转到“输入和输出”选项卡并将输出端口的名称从“输出 0”更改为“唯一”。还将 ExclusionGroup 更改为 1。
添加一个新的输出并将其命名为 Duplicate 并将 ExlusionGroup
更改为 1。要将这个新的输出端口连接到输入端口,请更改 SynchronousInputID
属性并选择输入端口。
编辑脚本并复制以下代码。此脚本使用反射来获取所有选定的列,这样您在更改输入列时就不必更改脚本。但请阅读编码注释。
// C# code
// This script automaticly compares the selected columns,but there is one 'bug':
// You have to edit and close this script again if you change input columns.
using System;
using System.Data;
using System.Reflection; // Added
using System.Text; // Added
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
// Create a variable to store the concatenated values for the previous row
string previousRow = "";
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
// Create a variable to store the concatenated values of current row
StringBuilder currentRow = new StringBuilder();
foreach (PropertyInfo p in Row.GetType().GetProperties())
{
// We can't use the _IsNull columns,so ignore them. Also ignore the new output column
if ((p.Name.ToLower().EndsWith("_IsNull") == false) && (p.Name.Equals("Duplicate") == false))
{
try
{
// Concatenate value as string to variable
currentRow.Append(p.GetValue(Row,null).ToString() + "|");
}
catch (ArgumentException)
{
// If the value is NULL (empty) then you can't get the value of it
currentRow.Append("NULL|");
}
catch (Exception ex)
{
// Raise error because something unexpected went wrong
bool pbCancel = false;
this.ComponentMetaData.FireError(0,"MarkDuplicates",p.Name + ": " + ex.Message,string.Empty,out pbCancel);
}
}
}
// Check if the current row and previous row are the same
if (currentRow.ToString().Equals(previousRow))
{
// Redirect to duplicate output
Row.DirectRowToDuplicate();
}
else
{
// Redirect to unique output
Row.DirectRowToUnique();
}
// Fill previous row with current value for next check
previousRow = currentRow.ToString();
}
}
或 VB.Net :
' VB.Net code
' This script automaticly compares the selected columns,but there is one 'bug':
' You have to edit and close this script again if you change input columns.
Imports System
Imports System.Data
Imports System.Math
Imports System.Reflection ' Added
Imports System.Text ' Added
Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
<Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute> _
<CLSCompliant(False)> _
Public Class ScriptMain
Inherits UserComponent
' Create a variable to store the concatenated values for the previous row
Dim previousRow As String = ""
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
' Create a variable to store the concatenated values of current row
Dim currentRow As StringBuilder = New StringBuilder()
Dim p As PropertyInfo
For Each p In Row.GetType().GetProperties()
' We can't use the _IsNull columns,so ignore them. Also ignore the new output column
If ((p.Name.ToLower().EndsWith("_IsNull") = False) And (p.Name.Equals("Duplicate") = False)) Then
Try
' Concatenate value as string to variable
currentRow.Append(p.GetValue(Row,Nothing).ToString() + "|")
Catch ex As ArgumentException
' If the value is NULL (empty) then you can't get the value of it
currentRow.Append("NULL|")
Catch ex As Exception
' Raise error because something unexpected went wrong
Dim pbCancel As Boolean
Me.ComponentMetaData.FireError(0,String.Empty,pbCancel)
End Try
End If
Next
' Check if the current row and previous row are the same
If (currentRow.ToString().Equals(previousRow)) Then
' Redirect to duplicate output
Row.DirectRowToDuplicate()
Else
' Redirect to unique output
Row.DirectRowToUnique()
End If
' Fill previous row with current value for next check
previousRow = currentRow.ToString()
End Sub
End Class
添加两个目的地并将 Data Flow Paths
连接到目的地。连接时必须选择输出端口。
不要忘记添加一些用于测试目的的数据查看器。