如何使用SSIS从平面文件中删除重复行?

6
首先,我想说能够从一个平面文件中提取1700万条记录,将其推送到远程数据库并且只需要7分钟就完成这一操作是非常惊人的。SSIS确实很棒。但是现在我已经将数据放到了数据库中,如何去重呢?
更好的做法是,我想要从平面文件中删除重复项,并将它们放回另一个平面文件中。
我考虑使用:
数据流任务
文件源(带有相关的文件连接)
循环容器
包含一些逻辑以判断是否存在另一行的脚本容器
谢谢,这个网站上的每个人都非常有知识。
更新:我找到了这个链接,可能有助于回答这个问题

当你说“重复”时,你是指记录完全相同还是主键相同的记录? - AJ.
9个回答

23

使用排序组件。

只需选择您希望按哪些字段对加载的行进行排序,在左下角您会看到一个复选框,可用于去除重复项。该框基于排序条件仅删除任何重复的行。因此,在下面的示例中,如果我们仅按第一个字段排序,则这些行将被视为重复:

1 | sample A |
1 | sample B |

6
我建议使用SSIS将记录复制到临时表中,然后创建一个任务,根据情况使用Select Distinct或Rank来选择重复项,将它们导出到平面文件并从临时表中删除。最后一步是将记录从临时表复制到目标表。
确定重复项是SQL擅长的,但平面文件不太适合。在您提出的情况下,脚本容器会加载一行,然后必须将其与1700万条记录进行比较,然后加载下一行并重复……性能可能不是非常好。

5

平面文件数据源 --> 聚合(按照你想要唯一的列来分组) --> 平面文件目标


2

一种比较简单的解决方法是设置一个跨越所有列的组合键来确保唯一性。然后在数据目标形状上,配置任务以忽略错误。所有重复插入的数据将被丢弃。


2
我们可以使用查找表来实现这一点。就像SSIS提供了两个DFS(数据流转换),即模糊分组和模糊查找。

2

策略通常取决于暂存表有多少列。列越多,解决方案就越复杂。你提供的链接文章给出了一些非常好的建议。

除了其他人已经说过的所有内容之外,我要补充的是,带有日期和时间值的列将使这里介绍的一些解决方案变得困难。

我想到的一个解决方案是:

SET NOCOUNT ON

DECLARE @email varchar(100)

SET @email = ''

SET @emailid = (SELECT min(email) from StagingTable WITH (NOLOCK) WHERE email > @email)

WHILE @emailid IS NOT NULL
BEGIN

    -- Do INSERT statement based on the email
    INSERT StagingTable2 (Email)
    FROM StagingTable WITH (NOLOCK) 
    WHERE email = @email

    SET @emailid = (SELECT min(email) from StagingTable WITH (NOLOCK) WHERE email > @email)

END

使用这种方法与使用游标相比,在去重时速度要快得多,并且不会使服务器的 CPU 占用过高。使用此方法时,请将来自文本文件的每个列分别存储在它们自己的变量中。在循环之前和循环内部使用单独的 SELECT 语句,然后将它们包含在 INSERT 语句中。我已经成功地使用了这种方法。


Hector,当涉及到这个SSIS项目时,你将成为我的救星!非常感谢你! - RyanKeeter
我很高兴能为您服务。;) - Hector Sosa Jr

2

在平面文件上执行此操作,我使用Unix命令行工具sort:

sort -u inputfile > outputfile

很遗憾,Windows的排序命令没有唯一的选项,但您可以尝试从以下网站下载排序工具: (我没有尝试过它们,所以不能保证结果)。
另一方面,如果您想在记录加载到数据库时进行去重,可以在数据库表的关键字上创建一个带有ignore_dup_key参数的唯一索引。这样做会在加载时高效地使记录唯一。
CREATE UNIQUE INDEX idx1 ON TABLE (col1, col2, ...) WITH IGNORE_DUP_KEY

1
发现这个页面 链接文本 可能值得一看,尽管有 1700 万条记录可能需要花费一些时间。

1
我建议在目标服务器上加载一个暂存表,然后将结果合并到目标表中。如果您需要运行任何卫生规则,则可以通过存储过程来完成,因为这样做的性能比通过SSIS数据流转换任务要好。此外,去重通常是一个多步骤的过程。您可能想要进行以下去重操作:
  1. 不同的行。
  2. 像名字、姓氏、电子邮件地址等不同列组的不同组。
  3. 您可能想要针对现有的目标表进行去重。如果是这种情况,则可能需要包括NOT EXISTS或NOT IN语句。或者您可能想要使用MERGE语句和源的子查询来更新原始行的新值。这通常最好使用CTE和ROW_NUMBER()生成顺序和反向顺序列,如下面的示例所示:

.

WITH    
    sample_records 
    (       email_address
        ,   entry_date
        ,   row_identifier
    )
    AS
    (
            SELECT      'tester@test.com'
                    ,   '2009-10-08 10:00:00'
                    ,   1
        UNION ALL

            SELECT      'tester@test.com'
                    ,   '2009-10-08 10:00:01'
                    ,   2

        UNION ALL

            SELECT      'tester@test.com'
                    ,   '2009-10-08 10:00:02'
                    ,   3

        UNION ALL

            SELECT      'the_other_test@test.com'
                    ,   '2009-10-08 10:00:00'
                    ,   4

        UNION ALL

            SELECT      'the_other_test@test.com'
                    ,   '2009-10-08 10:00:00'
                    ,   5
    )
,   filter_records 
    (       email_address
        ,   entry_date
        ,   row_identifier
        ,   sequential_order
        ,   reverse_order
    )
    AS
    (
        SELECT  email_address
            ,   entry_date
            ,   row_identifier
            ,   'sequential_order'  = ROW_NUMBER() OVER (
                                        PARTITION BY    email_address 
                                        ORDER BY        row_identifier ASC)
            ,   'reverse_order'     = ROW_NUMBER() OVER (
                                        PARTITION BY    email_address
                                        ORDER BY        row_identifier DESC)
        FROM    sample_records
    )
    SELECT      email_address
            ,   entry_date
            ,   row_identifier
    FROM        filter_records
    WHERE       reverse_order = 1
    ORDER BY    email_address;

在去重文件方面,有很多选项可供选择,但最终我建议在目标服务器上加载暂存表后,在存储过程中处理此问题。在清理数据之后,您可以将其合并或插入到最终目的地。


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