和其它程序一样,SSIS包同样需要健壮,稳定的运行,这样的程序才有可靠性和可伸缩性。SSIS提供了如下方面的支持:
1.事务: 可以对一个程序包设置成一个或者多个事务,甚至可以对两个程序包设置成一个事务。为了保证数据的一致性,你还可以DTC事务或者SQL Server引擎级的事务。
2.检查点: 用来记录一个程序包出错时任务的运行情况,以便程序包再次启动时,直接从发生错误的任务直接执行.
3.错误输出:即使再完美的程序也会有错误,尤其对于数据流中的任务来说,及有可能由于格式,类型等问题,导致这一行的发生错误。SSIS允许针对这样的每行进行错误输出处理,你可 以进行修复或者记录,当然你也可以忽略。
4.优先约束:在控制流中,你不仅可以使用一个任务的成功,失败或执行结束作为条件来执行下面的任务,甚至可以使用表达式来做为条件。
5.事件处理: 在SSIS包中,事件处理是和控制流,数据流相并列的。在这里,你可以对程序包,任务或者容器的不同的事件进行处理,事件还可以用于设置断点并控制日志记录。我们可以使用OnError事件来捕获错误.
6.日志记录: 可以在运行时,记录指定的事件信息,可以保存在本地文本或者XML文件中,也可以保存到数据库中,或者是Windows EventLog,Profiler文件中,甚至你可以扩展自己的日志记录。
需求:
为了和其它系统集成,AdventureWorks系统的产品价格实时更新,需要从一个指定的URL下载一个压缩文件,解压后,对这个数据文件进行分析导入。由于下载的文件是由一个非程序维护的文件,由于人为因素,里面的数据格式有可能有错误,但是要求忽略这样的数据。另外,如果处理失败,需要对保留历史文件。如果发生错误,需要发送错误Email.同时需要保留程序日志,以便查找错误原因。
实现:
1. 在你的D盘下面新建一个ProductPrice文件夹来存放压缩和数据文件,在下面新建一个bak目录存储备份文件。
2. 创建一个SSIS包,同时新建以下变量:
变更名 | 类型 | 值 | 说明 |
filePath | String | D:\ProductPrice\ | 数据根目录 |
dataPath | String | D:\ProductPrice\ProductList.txt | 解压后数据文件 |
backPath | String | @[User::filePath] + "bak\\" + (DT_WSTR, 4) YEAR( GETDATE() ) + "-" + (DT_WSTR, 2) MONTH( GETDATE() ) + "-" + (DT_WSTR, 2) DAY( GETDATE() ) + ".zip" |
需要将EvaluateAsExpression设置为True 主要用于存储未执行成功的压缩数据文件,其名称为当前日期(由表达式计算出当前日期) 比如:D:\ProductPrice\bak\2009-9-20.zip |
WinRar | String | C:\Program Files\WinRAR\WinRAR.exe | WINRAR的安装目录 |
zipPath | String | D:\ProductPrice\Price.zip | zip压缩文件下载路径 |
erroCount | Int32 | 0 | 数据文件解析出错的行数 |
3.新建一个OLE DB连接,连接我们的AdventureWorks数据库.
4.新建一个Http连接,URL填写我们的远程的压缩文件的位置(当然,这里也可以使用变量).
当然,实际中这里可能需要使用凭据或者证书。
5.在控制流中添加脚本任务,用来完成我们下载文件,设置读变量User::zipPath。编辑脚本:
public void Main()
{
// TODO: Add your code here
object nativeOjbect = Dts.Connections[0].AcquireConnection(null);
HttpClientConnection connection = new HttpClientConnection(nativeOjbect);
string filename = Dts.Variables["zipPath"].Value.ToString();
connection.DownloadFile(filename,true);
Dts.TaskResult = (int)ScriptResults.Success;
}
执行程序包,你会发现已经能够将rar文件下载到本地的文件夹中.
6.现在我们来完成解压的任务,首先需要确认本地已经安装了WinRar应用程序。添加执行进程任务,设置属性WindowStyle属性为Hidden,进行表达式设置,分别对属性进行如下设置:
属性 | 表达式 | 说明 |
Executable | @[User::WinRar] | 可执行程序Winrar路径 |
WorkingDirectory | @[User::filePath] | 执行目录 |
Arguments | "e " + @[User::zipPath] + " *.txt -y " | WinRar命令行参数,解压@zippath文件中所有的.txt文件,y代表如果存在直接覆盖 |
7.新建一个文件连接,指向我们的D:\ProductPrice\ProductList.txt文件,选中Unicode,对列进行设置。但是,我们的文件路径是变量,不能够写死在这里,所以在属性里对其Expressions设置,设置ConnectionString的值为@[User::dataPath].
8.添加数据流任务.在数据流任务中首先添加一个平面文件源,连接我们的刚才新建的文件连接。文件中只有两个列,一个是产品编号,一个是产品价格。两个都为DT_WSTR类型。
PrdocutNumber ListPrice
AR-5381 22.0
BA-8327
BE-2349 12
BE-2349
BE-2908 122.2
添加数据转换任务,我们将产品价格转换为数字类型:
执行程序包,出错了,错误就发生成我们的数据转换任务,对于空的字符串,不能够将其转换成数字类型,怎么办?点击左下角的配置错误输出,这里我们可以指定行级错误,对出错的行选择:
1.组件失败: 导致整个任务执行失败.
2.忽略失败: 忽略这个行的出错,对于这个任务,忽略的行的PriceList的值为NULL
3.重新定行: 失败的数据,可以重新定义输出.
在这里,我们选择重新定行:
同理,我们也可以对上面的文件连接进行同样的错误输出处理,以防止在读取文件时,由于数据原因发生错误。
9.添加一个OLE DB命令,完成数据的更新。SqlCommand为:
UPDATE Production.Product SET ListPrice =? WHERE ProductNumber=?
在列映射栏里,对两个参数进行映射,注意前后的顺序。
10.添加行计数任务,注意,从数据转换任务的下面拖拉红色的错误输出到行计数下面,使我们出错的行数据流入这个任务。设置行计数任务的变量为@errorcount.再次执行包,错误没有了,发现数据转换下面已经分成3行的正确走向,和2行的错误走向。
11.添加对文件的处理。添加三个文件系统执行到控制流中,分析执行删除压缩文件,备份压缩文件,删除数据文件。注意,均使用变量的方式(具体见下载源文件).我们希望在数据流执行失败时执行备份压缩文件,在这里我们需要设置优先约束,默认的是绿色的完成时执行。右击连接,选择失败,
不管执行成功还是失败,我们都希望在最扣删除数据文件,我们又将两个文件系统任务同时指向了删除数据文件任务。而我们需要设定他们之间任务一个执行完成时即执行,象上面一样,右击选择编辑进入优先约束编辑器,我们选择逻辑或,即两个任务有一个执行成功即可:
12.我们来看一下上面设置的优先约束的效果。修改数据流中的数据转换任务先后为组件错误和重新定行:
13.虽着产品数量的增多,下载的压缩包太大,每次执行失败时,我们希望能够从执行失败的任务直接运行,而不用都要重新去下载和解压.我们来设置Checkpoings,检查点来完成这样的功能。首先设设置包的三个属性:SaveCheckpoings为True,CheckpoingUsage为IfExists ,CheckpointFileName选定设置一个txt文件即可。 另外需要注意的是:如果我们希望一个任务可以设置检查点的话,这个任务的FailPackageOnFailure属性必须为True。我们设置分析数据更新价格的数据流的FailPackageOnFailure属性为True,(这其实和我们上面的优先约束是有冲突的,因为现在话,即使这个任务执行失败,也就直接导致整个包执行失败了,也就不会执行下面的备份和删除任务了。这里我们只是为了分别演示不同的设置). 我们再次设置数据流中的数据转换为组件失败来导致整个数据流失败,运行包,检查一下刚才你所选择的CheckpointFileName里面有了很多数据。修改数据转换任务为重新定行,运行包时,下载产品价格压缩文件和解压文件两个任务没有运行,而是直接从分析数据更新价格开始的:
在实际中,我们可以使用检查点设置,在重新运行包时,不用再去重新运行没有发生错误的,而且非常消耗资源和时间的任务。
14.如果分析数据更新价格这个数据流下面的文件处理出错时,会怎么样?我们的价格更新还不会不会提交?我们把删除压缩文件的路径修改为user:filepath使其发生错误,经过对比发现,即使删除压缩文件这个任务发生了错误,上面的分析数据更新价格的任务也同样提交了更改,产品的价格已经更新成功了。而这其实不是我们希望看到的,我们希望ProductPrice下面的文件与我们数据库的更新处理保持一致,在下面的文件处理发生错误时,上面的数据库更新同样也不提交。这就需要事务了,需要设置程序包的事务属性和任务级的属性--TransactionOption对它的值作一个说明。
Support 如果父对象中已经存在事务,则加入.
Not Supported 即使存在一个事务,也不加入
Required 事务是必须的,如果存在,加入存在的。如果不存在,启动一个事务。
可以看到,我们现在的任务和包的TransactionOption的值都为Support,其实是没有启动事务的。那修改包的属性为Required,又出错了,不允许这样的设置,提示:
不支持当前的包设置。请更改 SaveCheckpoints 属性或 TransactionOption 属性。
其实CheckPoings和Transaction也是相互有冲突的。一个事务的容器是一个可以再重新启动切入的最小单位,整个事务要不都不执行,要不都执行,这才符合事务的特性。 为了减少两者之间的冲突,SSIS在一个容器(包也是一个容器)没有在一个任务中时,不去保存它的检查点信息。 另外,Checkpoints在遇到Foreach容器中也会有同样的问题,因为它不能够保存容器内部的信息。(这里有详细的说明http://technet.microsoft.com/en-us/library/ms140226.aspx)
同样,我们只是为了演示效果,先把SaveCheckpoints设置为False,把TransactionOption设置为True.再次运行包,对比前台的数据,发现当删除压缩文件失败时,即时显示更新价格任务执行成功,价格也没有更新,此时程序包已经启动了分布式事务。
15.是不是整个包不会出错了?当然不是,如果远程的下载地址不可访问或者连接超时,如果本地磁盘空间不足,如果更新数据库时发生错误,都有可能导致整个包再次发生错误。而我们希望在发生这样的错误时,能够主动的通知我们。并告诉我们具体的错误信息。切换成事件处理程序,有两个选择下拉框,可执行文件和事件处理程序。在这里我们只对Package的OnError进行响应,直接点击下面的创建,在事件处理中我们可以使用与控制流同样多的任务类型.
发送邮件我们使用和上次同样的方法,使用脚本发送邮件,只需要一个脚本任务即可,具体的请看:http://www.cnblogs.com/lonely7345/archive/2009/09/03/1559579.html只需要获取错误信息作为邮件的内容.
string body = "Package failed error: " + Dts.Variables["ErrorDescription"].Value.ToString();
16.我们最后来设置日志记录,点击上面的SSIS菜单,选择日志记录,这里同样可以选择容器,还可以选择日志的提供类型,切换到详细信息,我们可以选择需要记录的事件:
我们选择基于文本文件的日志提供程序,并创建存放日志的文件连接,选择OnError和OnWarning为需要记录的事件,执行包,查看Log.txt发现里面有了详细的错误信息。我们同样可以在这里扩展提供程序类型,使之能够发邮件,达到和事件处理同样的效果,而且更具有通用性,这里也不深入下去了。
结束:
采用SSIS完成一个实际的功能来介绍了SSIS包的事务,检查点,事件处理,错误输出,优先约束,日志记录等方面,同时也介绍了SSIS的表达式,执行进程任务,下载远程文件等很多细节.SSIS确实强大,在这么短的几次文章当中很难涵盖它的方方面面,虽然结束的有些匆忙,但是很高兴这一系列的SSIS到这次就结束了,希望这些简单的例子能够对入门ssis的朋友有些帮助,也希望我们能够分享它的强大。下次将开始SQL Server BI Step by Step SSRS --- reporting service 2008.
下载:
http://files.cnblogs.com/lonely7345/DownloadRssNews.rar
作者:孤独侠客(似水流年)
出处:http://lonely7345.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。