本篇文章是Integration Services系列的第三篇,详细内容请参考原文。
增量加载是什么
增量加载仅加载与先前加载差异的。差异包括:
->新增的行
->更新的行
->删除的行
通过它的性质,一个增量加载是可重复执行的,意味着你可以一遍遍的执行而不会损坏数据。更有意思是,加载被设计为执行多次,而不会引起服务器进行不必要的或重复的工作。
在这一篇及接下来的两篇,我们会学习增加加载中的新增、更新、删除行。本篇我们集中在新增上。
To The Keyboard
让我们打开第二篇SSIS数据泵中的My_First_SSIS_Project项目,当你打开项目后,你需要打开解决方案管理器然后双击Package.dtsx来打开SSIS包设计器。
图3.1 打开SSIS包设计器
在图3.1中打开数据流任务编辑器(你可以点击数据流页签,或双击数据流任务,或右击数据流任务然后选择编辑)。一旦打开编辑器,我们可以看到OLE DB源适配器通过数据流路径和OLE DB目标适配器连接:
图3.2 数据流任务下的源和目标
如果你按照第二篇的步骤操作过,那么你从AdventureWorks2012.Person.Person表加载19972行数据到AdventureWorks2012.dbo.Person表。
问题:如果你重新执行SSIS包会发生什么?答案:它会从Person.Person表加载相同的19972行到dbo.Person表。不要不相信我,让我们测试下,在SSMS中执行下面代码:
use AdventureWorks2012
go
select * from [dbo].[Person]
如果脚本返回行数大于19972,说明SSIS包不止执行1次。你可以用下面脚本清除dbo.Person表中的数据:
delete [dbo].[Person]
返回到BIDS然后按F5重新运行SSIS包,当数据加载完成数据流任务如图3.3所示:
图3.3 OLE DB源数据转移到OLE DB目标
返回SSMS并注释掉Delete语句。在Delete语句被选中的情况下,可以通过快捷方式注释语句,按下Ctrl+K,然后按下Ctrl+C键;取消注释,按下Ctrl+K,然后按下Ctrl+U键。
现在dbo.Person表中应该只有19972行记录。你可以用前面的查询语句检查。
如果BIDS调试器还在运行,调试菜单应该如图3.4红粗框所示;此时如果你查看控制流或任务流的工具箱,应该可以看到如图3.4红细框所示的提示信息:
图3.4 运行状态下的调试菜单和工具箱
这是因为BIDS调试器正在运行,可以在图3.4的右上角看到调试菜单中的"启动调试"按钮是灰色/禁用的,因为这个包已经在调试模式下执行。
有几种方式可以停止BIDS调试器。你可以点击调试菜单中的"停止"按钮(图3.4红粗框中);你可以同时按下Shift+F5;你可以点击BIDS->调试->停止调试
图3.5 停止调试
你也可以在包设计器的底部单击“连接管理器”选项卡下方的“链接”:
图3.6 连接管理器下的链接
选择任一方式停止BIDS调试器。
增量加载的目标
在我们开始做一些改变之前,让我们来定义一个增量加载的目标:
1、源新增(new)的数据插入(insert)到目标
2、源修改(changed)的数据更新(update)到目标
3、源移除(removed)的数据从目标中删除(delete)
本篇剩余篇幅将集中讨论第一种情况,源增加新行插入到目标。
检测新行
在我们开始之前,让我们回到SSMS并创建新行测试。通过思考,有一些方式可以建立这个测试:
1、在AdventureWorks2012.Person.Person(源表)加入新行。毕竟,在现实中行会被加到源,然后加载到目标。根据你的设置,这可以简单的完成。
2、从目标删除行
两种方法都能满足测试需求,源中部分记录不存在于目标。这里选择第2种方式,从目标中删除行。让我们在SSMS中执行下面代码:
use AdventureWorks2012
go
delete [dbo].[Person] where MiddleName Is NULL
这条语句将删除dbo.Person表MiddleName列为null的所有记录。当我执行这个语句,可以看到下面信息:
(8499 行受影响)
dbo.Person表中还剩下11473行。我们已经有测试条件,当前源中部分行在目标中不存在。
我们的测试条件存在,我们有新的行加载。增量加载新行要做的第一件事是检测新行。由于我们的源和目标是SQL Server表保存在同一个数据库,我们可以返回SSMS,然后执行一些T-SQL来识别新行。有很多方法可以完成这个。
方法1:使用not in我们可以查找记录在源(Person.Person),没有出现在目标(dbo.Person)。在SSMS的查询窗口执行下面的T-SQL语句:
use AdventureWorks2012
go
select FirstName
,MiddleName
,LastName
,BusinessEntityID
from Person.Person
where BusinessEntityID Not In (select EntityID from dbo.Person)
方法2:使用LEFT JOIN查询检测新行
use AdventureWorks2012
go
select src.FirstName
,src.MiddleName
,src.LastName
,src.BusinessEntityID
from Person.Person src
left join dbo.Person dest
on src.BusinessEntityID = dest.EntityID
where dest.EntityID is null
Breaking a Few Eggs
返回到BIDS中的My_First_SSIS_Project项目,你可能已经猜测到我们需要在OLE DB源和OLE DB目标之间添加组件。因此我们要做的第一件事是,删除数据流路径。右击数据流路径,点击删除:
图3.7 删除数据流路径
一旦删除数据流路径,Person目标适配器将出现一个错误提示(红圈中有一个白X)
图3.8 删除数据流路径后的任务流
这是什么意思?为什么现在出现?因为我们删除了之前配置的数据流路径。记得在第二篇图2.33,我们打开数据流路径编辑器并查看了元数据选项。那里包含了数据流任务传给OLE DB目标(Person)适配器的列信息。数据流路径是数据流任务和目标适配器的一个接口。
我们可以点击视图->错误列表,来查看错误的详细信息:
图3.9 删除数据流路径后的错误信息
OLE DB目标适配器报告缺失的输入列。它无法找到EntityID。但注意其他列也缺少。为什么OLE DB目标适配器没有报其他列的错呢?简单,它不关心有多少列缺失,它知道它需要所有。因此,只要有一个缺少,这就足以抛出错误。
添加查询组件
在数据流画布,OLE DB源和OLE DB目标之间拖放一个查找转换组件。你可能需要将OLE DB目标往下移动腾出空间。在OLE DB源单击,然后单击数据流路径(绿色箭头)并拖动到查找转换:
图3.10 添加查找组件
注意我使用的是SQL Server 2008 R2 Integration Services。在SSIS 2005以后查找转换发生了巨大的变化。以下适用于SSIS 2008和2008 R2。
查找转换所隐含的含义:它在另一个表、视图或查询中查找与流经该变换的行进行匹配。这里有几个关键概念,当我们配置查找时会指出。但一般的想法是"到其他表、视图、或查询,查找在某(些)列是否找到匹配。如果你找到了一个匹配,把这行(或这些行)其他的列返回。"这听起来比较简单,但有一些怪癖:
怪癖1:如果在数据流与查找对象(表、视图、查询)的列上没有找到匹配,查询转换默认会让转换失败
怪癖2:如果在查找对象中找到多条匹配,查询转换只会返回第一条匹配上的。
配置查找转换
双击查找,打开查找转换编辑器。默认显示常规页面,在常规项的顶部是缓存模式配置。
如果你使用的是SSIS2005,你可以在查找转换的属性列表中找到缓存模式选项。缓存模式属性控制实际查找操作何时以及如何发生。
无缓存模式:每行通过转换都会发生查找操作。每当一行传到查找,该转换就在查找对象(表、视图或查询)上执行一个查询;并将任何返回值添加到行中。
完全缓存模式:在数据流任务执行前,查找操作尝试加载查找对象(表、视图或查询)中的所有记录到缓存中。你有没有注意到"尝试"这个词?如果查找对象返回很大的数据集,或者服务器本身内存紧张,查找将会失败。查找缓存holds配置的表、视图或查询的数值。当他们通过转换时缓存中找到的匹配值就添加到行。
在完全缓存模式由于内存紧张导致查找转换失败,你该如何处理呢?一个选择是使用无缓存模式,另一个选择是使用部分缓存模式。还有其他的选择,但已超出本篇文章的范围。
部分缓存模式:当行传入时,转换首先在查找缓存中查找匹配。如果缓存中没有匹配就发生查找操作。匹配的数据加到行和查找缓存。如果另一行通过转换查找相同的匹配列,直接从查找缓存中获取匹配数据。
本例中选择默认配置,完全缓存。查找数据集非常小(19972行)。我会监控这个SSIS包的性能,如果需要就会适当调整。
接着,将"指定如何处理无匹配项的行"更改为"将行重定向到无匹配输出"
图3.11 查找转换编辑器常规页
这是SSIS2008/2008R2查找转换的一个很好的补充。在这个更新版本的查找中,有很多的“绿色”(有效的或非错误)输出,匹配输出和不匹配输出。
点击连接页设置OLE DB连接管理器到127.0.0.1,5377.AdventureWorks2012.sa。类似OLE DB源适配器,我们给连接管理器配置一个接口;同样我们需要选择表或键入查询语句从连接管理器配置的实例.数据库中获取数据。本例中键入下面T-SQL语句:
SELECT [EntityID]
,[FirstName]
,[LastName]
,[MiddleName]
FROM [dbo].[Person]
图3.12 查找转换编辑器连接页
点击列页。在页的右上部分有一些表格形式的表。左边的一个标记为可用输入列。这包含查找转换的输入列列表(记住,查找转换是连接到OLE DB源适配器的输出,这些列就是从那来的)。另一个网格标记可用查找列。这些是连接页上配置的表、视图或查询的列。
在可用输入列上点击EntityID列,然后拖动到可用查找列的EntityID列上。当你松开鼠标时就会在可用输入列的EntityID列和可用查找列的EntityID列之间出现一条直线,类似于join中的on子句,实际就是一个映射关系。
可用查找列旁边有复选框,在网格的顶部有一个选择所有的复选框。If the Lookup Transformation is similar to a Join, the checkboxes are a mechanism for adding columns from the joined table to the SELECT clause.在本例中,不需要检查任何列如图3.13所示:
图3.13 映射列
我们已经配置了查找转换,匹配存在于数据流管道与目标表中的记录。数据流管道中记录来自OLE DB源适配器,是从Person.Person表加载到数据流中。目标表是dbo.Person,我们通过查找转换编辑器中的连接页配置访问。我们配置查找转换通过对比目标表中的EntityID列和源表中的EntityID列。我们配置查找转换处理无匹配项的行时,将行重定向到无匹配的输出。如果在目标和源表中找到匹配行,查找转换将把这些行发送到匹配输出。
点击确定关闭查找转换编辑器。然后点击查找转换拖动其底部的绿数据流路径到OLE DB目标,弹出的对话框中选择"查找无匹配输出"。
图3.14 选择输入输出
让我们回顾一下我们在这里所做的。它能重复,这是一个非常重要的。
为什么选择"查找无匹配输出"?在SSIS2008/2008r2中,查找转换提供内置输出来捕获存在于源表(Person.Person表),但不存在于查找表(dbo.Person表)——查找无匹配输出。
让我们看一下数据流任务,检查添加查找转换的影响。右键单击OLE DB源适配器和查找转换之间的数据流路径,然后单击“编辑”:
图3.15 编辑数据流路径
当打开数据流路径编辑器,点击元数据页。路径数据元如图3.16所示:
图3.16 源和查找之间的数据流路径编辑器
这些列来自OLE DB源将进入到查找转换。关闭这个数据流路径编辑器。右击查找和OLE DB目标之间的数据流任务,然后选择编辑打开另一个数据流路径编辑器:
图3.17 查找和目标之间的数据流路径编辑器
它们是相同的!查找转换的无匹配输出是查找转换输入的一个拷贝。如果我们没有找到匹配,我们希望通过查找转换的无匹配输出传送所有的数据列给后续操作。关闭数据流路径编辑器。
当你完成,数据流任务画布将如图3.18所示:
图3.18 数据流任务画布
让我们测试,按F5。你应该能看到绿色窗体:
图3.19 第一次执行SSIS包
我们刚刚加载了8499行新数据。这8499行新数据来自哪里。是否记得:为了模拟新行,我们从dbo.Person表中删除8499行。我们只是重新加载它们。
现在你可以做些有趣的事情,点击调试菜单中的重新启动按钮:
图3.20 重新启动SSIS包
点击重新启动按钮,SSIS包会停止调试并重新启动调试。这次,注意查找转换在目标表中找到了源表中的所有行:
图3.21 第二次执行SSIS包
因为查找转换找到了所有匹配行,它们不会被发送到无匹配输出。
总结
我们已经完成了一组目标。首先,我们建立了一个加载器,只将源表中的新行添加到目标表。其次,我们所建的加载器是可重复执行的,它并不是将一堆复本复制到目标。
在离开这个主题之前,我想强调的是,你已经建立了一个有用的SSIS包。生产环境中有很多类似加载数据的SSIS包。增量加载有时只加载新行,比如历史每日货币兑换率的表,表中的数据是不会随时间变化的。每一天结束它就固定了。另外源中的数据变更或快或慢,增量加载模式允许你设置一年一次、一分钟一次或任何时间间隔(周期/不周期性)加载数据。只有新行被加载到目标。