SSIS学习使用十七:多个灵活的来源位置

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

翻译参考

本文主要参考翻译自 The Stairway to Integration Services 系列文章的原文 Multiple Flexible Source Locations – Level 17 of the Stairway to Integration Services,目的在于对 SSIS 有一个全面清晰的认识,所有内容在原文的基础上进行实操,由于版本差异、个人疑问等多种原因,未采用完全翻译的原则,同时也会对文中内容进行适当修改,希望最终可以更有利于学习和了解 SSIS,

感谢支持!

介绍

在本文中,我们通过将数据从多个文件加载到不同的主题区域来实现对SSIS参数、变量的综合运用。

在开始之前,请单击此链接andyweather.com/data/Weathe…

压缩文件中的数据以“MMMYY”格式存储在每个MonthYear文件夹中。天气数据存储在MonthYear文件夹下的子文件夹中。 Dec08和Feb09的MonthYear文件夹包含一个名为TH的子文件夹。 Apr09、Jun09和Aug09的MonthYear文件夹包含两个子文件夹:TH和WIND。

每个TH文件夹都包含一个名为sensor1-all.csv的文件。这些文件表示在2008年12月至2009年8月之间收集的温度和湿度数据。这些文件是累积的——2009年2月文件包括2008年12月中的所有数据,以及2008年12月至2009年2月之间添加的记录,而2009年4月文件包括所有来自2008年12月中的数据以及2008年12月至2009年4月之间添加的记录。所有三个文件的记录都类似于如下所示:

Date,Time,Min T,Max T,Average T,Min H,Max H,Average H,Comfort zone,Min DP,Max DP,Average DP,Min HI,Max HI,Average HI,Low Batt

2008-12-25,19:00,8.5,10.9,9.71,32,36,33,2,-6.0,-5.0,-5.71,--,--,--

2008-12-25,20:00,6.3,8.5,7.21,36,40,38,2,-6.0,-5.0,-5.95,--,--,--

2008-12-25,21:00,5.3,6.7,6.37,39,43,40,0,-6.0,-6.0,-6.00,--,--,--
复制代码

每个WIND子文件夹包含两个名为1day.csvall.csv的文件,

我们将添加一个新的SSIS包,以从每个MonthYear\WIND文件夹中的all.csv文件加载数据。

但首先,让我们看一下LoadWeatherData.dtsx包。我怀疑新数据在LoadWeatherData.dtsx包中不能很好地运行。我们首先要确保名为SourceFolder的项目参数指向正确的文件夹。

I am building my version of the SSIS project in separate folders so I can save the state of the project at the completion of each article. You are probably not doing this, and that’s ok.

打开项目参数Project.params,确保SourceFolder参数值指向正确的路径:

如果你一直从一开始跟随演练,现在LoadWeatherData.dtsx包的"控制面板"类似如下:

"DFT Stage Temperature and Humidity"数据流任务的棕色点表示断点。

加载温湿度数据时,由于WIND文件数据导致出错

如果你解压了"WeatherData_Dec08_Aug09.zip"文件到之前的数据文件中(即SourceFolder项目参数指定的文件夹),"FOREACH Temperature File"Foreach循环容器会发现新的WIND文件,并且这会导致"DFT Stage Temperature and Humidity"数据流任务失败。

为什么?"FOREACH Temperature File"Foreach循环容器使用SourceFolder项目参数动态设置Foreach文件枚举器的Directory属性(可以在Foreach循环容器编辑器的“收集”——Collection页上找到)。文件规格(File Specification)设置为检索在SourceFolder项目参数指定的文件夹及任何子文件夹中,查找满足所有逗号分隔值的文件("* .csv"文件)的完全限定文件名(the fully qualified file name),如图所示:

按照当前配置,"FOREACH Temperature File"Foreach循环容器将返回WIND子文件夹中的文件。这将导致"DFT Stage Temperature and Humidity"数据流任务产生问题,因为它被配置为仅加载温度和湿度(temperature and humidity)数据。

让我们看看它是如何破裂的,然后逐步解释它破裂的原因。

在SSIS调试器中执行LoadWeatherData.dtsx。当达到断点时,就像在"SSIS学习使用十:灵活的来源位置"中所做的那样,查看“局部变量”窗口(“Debug”->“Windows”->“Locals”——需要在debug模式下)。展开“变量”节点并滚动,直到可以查看User::SourceFileName变量的值。

注意,当“FOREACH Temperature File”Foreach循环容器遇到WIND子文件夹中的第一个csv文件时,包执行失败——文件的完整路径会推入User::SourceFileName SSIS变量–如图所示:

WIND文件名中的文件只有一个条目,与温度和湿度文件的布局不匹配。在LoadWeatherData.dtsxSSIS包中,错误由"FFSrc Temperature and Humidity"平面文件源适配器产生。错误文本是:"跳过数据行时出错"(An error occurred while skipping data rows)。

WIND数据不能被"FFSrc Temperature and Humidity"平面文件源适配器读取,在当前状态运行SSIS包会生成一个错误。

为什么加载失败?

在设计时创建平面文件连接管理器时,平面文件的格式(其架构)将被读取到平面文件连接管理器中。这仅在设计时或在开发期间发生。

在运行时,可以使用面向对象的开发人员熟悉的术语“不可变”(immutable,即Immutable_object,不可变对象)来描述平面文件连接管理器。声明一个对象是不可变的,意味着它不能更改(在大多数情况下…允许某些属性更改,但不要分散注意力……)。

平面文件连接管理器不是数据流中唯一的不可变对象。数据流管道(data flow pipeline)也是不可变的。作为SSIS开发人员,我们(通常)使用数据流路径与数据流管道进行交互以连接数据流对象。

因此,有一个问题:如果另一个文件具有相同的格式(模式),是否可以由相同的平面文件连接管理器和数据流任务加载该文件,就像连接管理器和数据流为该文件设计一样?

答案是“是”。我可以听到你在想,“安迪,定义等效项”。define equivalent

  • 字段数必须相同。
  • 数据类型必须与平面文件连接管理器中定义的数据类型匹配,或者必须隐式强制于平面文件连接管理器中定义的数据类型。
  • 数据长度必须等于或小于平面文件连接管理器中定义的长度。

请记住,除非每一列中的数据在所有平面文件中都具有相同含义,否则使用相同的连接管理器和数据流加载另一个文件将以非常聪明的方式污染数据库。

修正错误

你可能会想,"如何修正错误呢,安迪?",这是一个优秀的问题。我们必须修改文件规格,排除WIND逗号分隔值的文件,仅仅包含温度和湿度的逗号分隔值文件。

仅加载温湿度数据

如果LoadWeatherData.dtsxSSIS包仍在调试器中运行,请停止调试器。打开“FOREACH Temperature File”Foreach循环容器编辑器,然后导航到“收集”页。将“Files”属性从“*.csv”更改为“sensor1-all.csv”,如8所示:

此更改将“FOREACH Temperature File”Foreach循环容器配置为仅返回名为“sensor1-all.csv”的文件,而不是返回任何具有“csv”扩展名的文件。温度和湿度文件的名称为“sensor1-all.csv”,其他天气数据文件的名称则不同。

关闭编辑器,然后在SSIS调试器中重新执行LoadWeatherData.dtsx SSIS程序包。这次包成功。它还将来自两个新的温度和湿度文件的数据加载到数据库中。

在执行过程中,出现如下报错:

错误: 变量“User::SourceFileName”已在写入列表中。一个变量只能向读取锁定列表或写入锁定列表中添加一次。 错误: 变量“(null)”已在写入列表中。一个变量只能向读取锁定列表或写入锁定列表中添加一次。

开始有些不知所措。后面查找"User::SourceFileName"变量,打开Foreach循环编辑器,在"变量映射"页中,发现User::SourceFileName指定了多次。删除多余的,只保留一个即可。(出现的原因不知)

Error: The variable "User::SourceFileName" is already in the write list. A variable can only be added once to the read lock list or write lock list.

LoadWeatherData.dtsxSSIS程序包是可以再次使用的功能,但是现在名称不正确。将其重命名为LoadTemperatureData.dtsx

加载WIND数据

设计

在添加包以加载WIND数据之前,让我们考虑一下设计。我是从右到左设计(right-to-left design)的忠实拥护者——从输出开始,然后一直回到输入(一个或多个),因为它可以帮助我确定设计中的主要步骤(或模块)。从新包的输出开始,找到目标数据库WeatherData,如图所示

我将WIND数据"暂存"在WeatherData数据库的一个表中。向后进行,暂存将通过新SSIS包中的“仅向前”(forward-only)增量加载数据流任务来完成,如图所示。

再往后一步,此数据流任务将在Foreach循环容器内,就像LoadTemperatureData.dtsx SSIS包中的Foreach循环容器一样,如图所示。

以上3个图中的构造图在功能上不准确(大多数框图(block diagrams)在功能上都不准确)。

提升项目连接管理器

LoadTemperatureData.dtsx包有一个用于WeatherData数据库的包连接管理器(package connection manager)。在项目部署模型中开发包时,SSIS 2012提供项目连接管理器(project connection managers)。并且,将包连接管理器转换为项目连接管理器非常容易。

要将包连接管理器升级为项目连接管理器,只需右键单击包连接管理器,然后单击“转换为项目连接”(Convert to Project Connection),如图所示。

点击"转换到项目连接"后,连接管理器会提升到项目连接管理器,连接管理器的名字会添加一个前缀"(project)"——"(项目)"

同时,新提升的项目连接管理器也会在解决方案资源管理器下的"连接管理器"(Connection Managers)节点列出:

这样可以节省新的SSIS包中的步骤。哪一步?让我们创建该包,看看吧!

构建LoadWindData.dtsx

在解决方案资源管理器中右键单击“ SSIS包”节点,然后单击“新建SSIS包”,然后重命名新包为LoadWindData.dtsx

新包将会在SQL Server Data Tools集成开发环境中打开。

注意,LoadWindData.dtsxSSIS包中可以使用名为“(项目)WIN-FR5GRQSCDPO.WeatherData”的项目连接管理器,而我们无需执行任何操作即可实现此目的。为什么?是因为项目中所有SSIS包都可以使用全部的项目连接管理器。多么酷啊!

将一个Foreach循环容器添加到LoadWindData.dtsxSSIS包的控制流中,并将其重命名为“FOREACH Wind File”。

打开"Foreach循环容器"的编辑器,然后导航到“集合”页。单击Enumerator Expressions属性。

点击Expressions属性集合的值文本框的省略号,打开属性表达式编辑器,从"Property"下拉列表中选择"Directory",

然后,点击"Expression"文本框的省略号,显示表达式构建器,展开"变量和参数"节点,点击$Project::SourceFolder并拖拽到"表达式"文本框

如果点击"计算表达式"按钮,将会看到项目参数$Project::SourceFolder当前设计时的值。

这是SSIS 2012的项目部署模型中项目级对象的另一个好处。与项目连接管理器类似,SSIS项目中的每个包都可以使用项目参数。同样很酷!

单击"确定"按钮关闭表达式生成器,然后单击"确定"按钮关闭属性表达式编辑器,返回到Foreach循环编辑器。我们希望使用此Foreach循环容器检索的是包含多天的Wind数据的文件。这些文件名为“all.csv”,可以在我们的天气数据文件的MonthYear文件夹的\WIND子文件夹中找到。

在Foreach循环编辑器的“文件”属性中,输入“all.csv”,还要选中“遍历子文件夹”(Traverse subfolders)复选框。如图所示

我们配置了Foreach循环容器,在名为$Project::SourceFolder的项目参数包含的路径(或其子文件夹)中搜索名为all.csv的文件。 Foreach循环每次查找并返回其中的一个文件。

当找到名为“all.csv”的文件时,我们需要将这些文件的完全限定路径发送到某个地方。让我们配置一个SSIS变量,使其包含Foreach循环容器每次迭代的源文件路径。

点击"变量映射"页,点击"Variable"下拉框,然后点击"新建变量"。

在"添加变量"窗口,确保"容器"属性设置为"包容器"——LoadWindData.dtsx

在"名称"(Name)属性中输入变量名:SourceFileName

单击“确定”按钮关闭“添加变量”窗口,返回到Foreach循环编辑器。

点击"确定"按钮关闭"Foreach Loop Editor"。

从控制流SSIS工具箱中拖拽一个数据流任务到Foreach循环容器,重命名数据流任务"DFT Load Wind Data":

双击"DFT Load Wind Data"数据流任务打开编辑器,添加一个平面文件源适配器并重命名为"FFSrc Wind Data"。

双击"FFSrc Wind Data"平面文件源适配器打开编辑器:

点击"Flat file connection manager"下拉列表旁边的"新建"按钮,创建一个新的平面文件连接管理器,并打开其编辑器。点击"File name"属性文本框旁边的"浏览"按钮,显示打开对话框,修改文件类型过滤为"*.csv",导航到\data\Apr09\WIND文件夹,选择"all.csv"文件

修改平面文件连接管理器的名字为"FFCM Wind Data"

点击"列"页,可以预览文件中数据。

点击"确定"按钮关闭平面文件连接管理器编辑器,在"平面文件源编辑器"的"列"页中,显示从Flat File Connection Manager到Flat File Source Adapter返回的列。

在右下方的网格中,找到“外部列”(External Columns)和“输出列”(Output Columns)。外部列从平面文件连接管理器提供给平面文件源适配器。我们可以更改这些列的名称,但不是此处,必须在平面文件连接管理器中才能更改。但是,我们可以通过编辑输出列名来更改列的别名。

我想从列名称中删除空格和“(m s)”文本。可以通过更改Output Columns列中的列名来做到这一点,修改完成后,列表格显示如下:

单击确定按钮关闭平面文件源编辑器。

从SSIS工具箱中拖动一个Lookup Transformation,并将数据流路径从“FFSrc Wind Data”平面文件源适配器连接到"查找转换"。

打开查找编辑器,在“常规”页面上,将“指定如何处理无匹配项的行”下拉列表更改为“将行重定向到无匹配输出”,如图所示。

点击"Connection"页

确认"OLE DB connection manager"下拉列表设置为"WIN-FR5GRQSCDPO.WeatherData"

我们将使用“查找转换”的一些功能来创建目标表。单击“使用表或视图”下拉菜单旁边的“新建”按钮。

与从OLE DB目标适配器创建表时一样,“创建表”窗口中显示的默认表名是“查找转换”的名称——此时为“查找”。

注意,列名反映了对平面文件源适配器中更改的输出列名(没有空格,并且已删除"(m s)"文本),修改语句中的表名为"StageWind",如图所示。

单击“确定”按钮以创建表,返回到“查找转换编辑器”。

我不确定为什么会发生这种情况,当按照刚才概述的步骤进行操作时,会去选择“使用SQL查询的结果”(Use results of an SQL query)选项,且该查询为select * from 及刚刚使用"新建"按钮创建的表的名称。

很好,因为我确实要选择该选项。但是让我们编辑查询,使其读取:

Select [Date], [Time]
From [dbo].[StageWind]
复制代码

切记:我们的数据每小时创建一次,并且永远不会更新。如果目标表中存在相同日期/时间组合的行,则表明我们先前已加载该行。

如果单击“预览”(Preview)按钮,在返回的“日期和时间”列中看不到任何数据(我们刚刚创建了该表,该表为空),但是这样可以测试查询,查看其构造是否足够返回一个空的数据集。

由于我们的数据永远不会更新,因此只需要检查新行或现有行。如果在目标表中找到日期/时间,则它是现有行。因此,我们仅在“Date”和“Time”列上检查匹配。

单击“列”页,然后将[Available Input Columns].[Date]列连接到[Available Lookup Columns].[Date]列,[Available Input Columns].[Time]列连接到[Available Lookup Columns].[Time]列,如图:

我们配置date/time不匹配的行从“查找转换”的"无匹配输出"中流出。简单地丢弃匹配行——匹配的行流到Lookup Transformation的"匹配输出"中,但是我们不会将"匹配输出"连接到数据流中。

单击“确定”按钮关闭“查找转换编辑器”。将"OLE DB目标适配器"拖到数据流上,并重命名为“OLEDBDest Stage Wind”。

将数据流路径从"查找转换"连接到“OLEDBDest Stage Wind” OLE DB目标适配器。出现提示时,从"输入输出选择"中,选择来自查找转换的“查找无匹配输出”(Lookup No Match Output),如图所示。

打开“OLEDBDest Stage Wind” OLE DB目标适配器的编辑器,确认"OLE DB connection manager"下拉列表设置为"WIN-FR5GRQSCDPO.WeatherData",并设置"Name of the table or the view"下拉列表为StageWind表,如下:

单击Mappings页完成将Available Input Columns自动映射到Available Destination Columns,如图所示:

单击确定按钮完成OLE DB目标适配器的配置。数据流应类似如图所示。

测试时间

在SSDT-BI调试器中执行LoadWindData.dtsx SSIS包之前,打开SSMS,连接到WeatherData数据库,然后执行以下查询:

Select * From dbo.StageWind
复制代码

查询结果应该为空。

在SSDT-BI调试器中执行LoadWindData.dtsx SSIS包,执行完成后,数据流应该类似如下图:

当包完成执行但调试器仍在运行时,单击SSDT-BI中的“Progress”选项卡。如果滚动到标有“Task DFT Load Wind Data”的节点,将发现数据流执行了("Start")3次,如图所示。

如果返回SSMS并重新执行测试查询,将在dbo.StageWind表中找到数据,如图所示。

总结

在本文中,我们利用SSIS参数,变量和Foreach循环容器从多个不同的源中加载数据。我们重新配置(并重命名)了原始程序包,以仅加载温度和湿度数据。

新的SSIS包(LoadWindData.dtsx)中的Foreach循环使我们可以将来自三个来源的风力数据归零,但我们可以轻松地从30个来源(或未知数目,或可变数目的来源)中加载数据。

Guess you like

Origin juejin.im/post/7035644915295993863