如何在SSIS包中优化Upsert(更新和插入)操作?

时间:2013-02-11 20:10:41

标签: sql-server-2008 ssis

我不是DBA,但我作为IT人员为一家小公司工作。我必须将数据库从登台复制到生产。我已经创建了一个SSIS包来执行此操作,但运行需要数小时。这也不是一个大型数据仓库类型的项目,它是一个非常简单的Upsert。我假设我是我设计它的薄弱环节。

这是我的程序:

  1. 截断登台表(EXECUTE SQL TASK
  2. 将数据从开发表中提取到暂存(Data Flow Task
  3. 运行数据流任务
    1. OLE DB Source
    2. Conditional Split Transformation (使用条件:[!]ISNULL(is_new_flag)
    3. 如果是新插入,如果是现有更新
  4. 模拟数据流任务几次以更改表/值,但流程是相同的。我已经阅读了几个关于OLE DB组件的更新速度很慢并尝试了一些但却没有让它快速运行的事情。

    我不确定要提供的其他细节,但我可以提供任何要求的内容。

2 个答案:

答案 0 :(得分:11)

使用SSIS 2008 R2的样本包,使用批处理操作插入或更新:

以下是用 SSIS 2008 R2 编写的示例包,演示了如何使用批处理操作在两个数据库之间执行插入,更新。

  • 使用 OLE DB Command 会降低程序包的更新操作速度,因为它执行批处理操作。每一行都单独更新。

该示例使用两个数据库,即 Source Destination 。在我的示例中,两个数据库都驻留在服务器上,但逻辑仍然可以应用于驻留在不同服务器和位置的数据库。

我在源数据库 dbo.SourceTable 中创建了一个名为 Source 的表。

CREATE TABLE [dbo].[SourceTable](
    [RowNumber] [bigint] NOT NULL,
    [CreatedOn] [datetime] NOT NULL,
    [ModifiedOn] [datetime] NOT NULL,
    [IsActive] [bit] NULL
)

此外,在目标数据库 dbo.DestinationTable 中创建了两个名为 dbo.StagingTable Destination 的表

CREATE TABLE [dbo].[DestinationTable](
    [RowNumber] [bigint] NOT NULL,
    [CreatedOn] [datetime] NOT NULL,
    [ModifiedOn] [datetime] NOT NULL
) 
GO

CREATE TABLE [dbo].[StagingTable](
    [RowNumber] [bigint] NOT NULL,
    [CreatedOn] [datetime] NOT NULL,
    [ModifiedOn] [datetime] NOT NULL
) 
GO

在表格 dbo.SourceTable 中插入约140万行,其中包含RowNumber列中的唯一值。 dbo.DestinationTable dbo.StagingTable 这些表一开始就是空的。表格 dbo.SourceTable 中的所有行都将 IsActive 标记设置为false。

Row counts - 1

创建了一个带有两个OLE DB连接管理器的SSIS包,每个管理器连接到SourceDestination数据库。设计控制流程如下所示:

  • 首先Execute SQL Task对目标数据库执行语句 TRUNCATE TABLE dbo.StagingTable 以截断登台表。

  • 下一节介绍如何配置Data Flow Task

  • 第二个Execute SQL Task执行以下给定的SQL语句,该语句使用 dbo.DestinationTable dbo.StagingTable 中的数据>,假设有一个唯一的密钥在这两个表之间匹配。在这种情况下,唯一键是 RowNumber 列。

要更新的脚本:

UPDATE      D 
SET         D.CreatedOn = S.CreatedOn
        ,   D.ModifiedOn = S.ModifiedOn 
FROM        dbo.DestinationTable D 
INNER JOIN  dbo.StagingTable S 
ON          D.RowNumber = S.RowNumber

Control Flow

我设计了数据流任务,如下所示。

  • OLE DB Source 使用SQL命令 dbo.SourceTable <从 SELECT RowNumber,CreatedOn, ModifiedOn FROM Source.dbo.SourceTable WHERE IsActive = 1 读取数据/ p>

  • Lookup transformation 用于检查表格中是否已存在RowNumber值 dbo.DestinationTable

  • 如果记录不存在,则会将其重定向到名为 OLE DB Destination Insert into destination table ,将行插入 dbo.DestinationTable

  • 如果记录存在,它将被重定向到名为 OLE DB Destination Insert into staging table ,将行插入 dbo.StagingTable 。登台表中的这些数据将在第二个“执行SQL任务”中用于执行批量更新。

Data Flow tab

要为OLE DB Source激活更多行,我运行以下查询以激活一些记录

UPDATE  dbo.SourceTable 
SET     IsActive = 1 
WHERE   (RowNumber % 9 = 1) 
OR      (RowNumber % 9 = 2)

Update rows - 1

首次执行包的方式如下所示。所有行都被定向到目标表,因为它是空的。在我的机器上执行包大概花了3 seconds

Execution 1

Execution time 1

再次执行行计数查询以查找所有三个表中的行计数。

Row counts - 2

要为OLE DB Source激活更多行,我运行以下查询以激活一些记录

UPDATE  dbo.SourceTable 
SET     IsActive = 1 
WHERE   (RowNumber % 9 = 3) 
OR      (RowNumber % 9 = 5) 
OR      (RowNumber % 9 = 6) 
OR      (RowNumber % 9 = 7)

Row counts - 3

包的第二次执行如下所示。先前在第一次执行期间插入的314,268 rows被重定向到临时表。 628,766 new rows直接插入目标表。在我的机器上执行包大概花了12 seconds。目标表中的314,268 rows在第二个执行SQL任务中使用登台表更新了数据。

Execution - 2

Execution time - 2

再次执行行计数查询以查找所有三个表中的行计数。

Row counts - 3

我希望能让您了解如何实施解决方案。

答案 1 :(得分:8)

我要看的两件事是你的插页(确保你使用“表格或视图 - 快速加载”或“表名或视图名称变量 - 快速加载”)和您的更新。

正如您已经正确确定的那样,更新逻辑通常是性能下降的原因,这是由于OLE DB组件为流经它的每一行触发单例更新。人们采取的通常做法是将所有更新写入临时表,就像插入逻辑一样。然后使用Data Flow Task跟进Execute SQL Task,以执行批量更新。

如果您想获得第三方工具,PragmaticWorks会提供Upsert destination