本篇文章是Integration Services系列的第八篇,详细内容请参考原文。
简介
在前面两篇文章,我们创建了一个新的SSIS包,学习了SSIS中的脚本任务和优先约束,并检查包的MaxConcurrentExecutables属性。我们检查、演示并测试优先约束赋值为"成功"、"完成"、"失败"时对工作流的影响。
在这一篇,我们继续深入SSIS工作流管理——学习SSIS变量和优先约束表达式。
关于变量
打开Precedence.dtsx包。点击BIDS顶部的SSIS,在弹出的下拉菜单选择变量:
图8.1 选择变量工具栏
变量窗体将会如图8.2展示:
图8.2 变量窗体
在变量窗体的顶部,有一排SSIS变量使用的工具栏。这些按钮分别是(从左到右):
添加变量:添加一个变量
删除变量:刚打开时,这个按钮是禁用的,因为Precedence.dtsx包还没有配置SSIS变量
显示系统变量:许多系统变量是SSIS包的属性。部分系统变量是组成SSIS的组件和对象的属性
显示所有变量:显示SSIS包中的所有变量,忽略作用域
选择变量列:如图8.3所示,点击此按钮来设置在变量窗体显示哪些列
图8.3 选择变量列
变量名称、作用域、数据类型、值默认是显示的。你不能取消变量名称列,但你可以决定选择作业域、数据类型和值,你也可以选择命名空间和变量值更改时引发事件。
变量和命名空间
SSIS包中有两个默认的命名空间:System和User.你不能够添加系统变量,但是你可以添加用户变量并且你可以创建新的名称空间。变量名称在作用域和命名空间下必须唯一。这就意味着你可以在User命名空间有一个MyVariable变量,它的完整名称是User::MyVariable(命名空间::变量名称)。在本例中User::MyVariable作用域是Precedence.dtsx包。为了创建User2命名空间,你需要显示命名空间列,然后编辑文本从User改成User2.当你在同一作用域访问具有相同名称但不同命名空间的变量时,你应该使用完整的变量名称,否则你会收到一个类似下面的错误:
因为在不同的命名空间中存在具有该名称的变量,指定命名空间限定名称以防止歧义。
添加变量
为了演示表达式和SSIS优先约束,让我们给Precedence.dtsx包添加一个SSIS变量。点击变量窗体中的添加变量按钮。一个Int32类型的变量就会创建:
图8.4 添加变量
重命名变量为MyBool并修改其数据类型为Boolean:
图8.5 重命名变量并修改数据类型
现在我们就可以在优先约束的表达式中使用这个变量。
表达式和优先约束
右击Script Task 1和Script Task 2之间的优先约束,然后点击"编辑",打开优先约束编辑器:
图8.6 优先约束编辑器
优先约束编辑器有两个控件。它们被标记为“约束选项”和“多重约束”。当我创建SSIS包,
我自然的标记控制流任务按照自上而下的顺序执行。配置任务自上而下执行的一个积极作用:优先约束编辑器上的控件布局与优先约束在控制流的物理布局保持一致。“约束选项”设置前面任务或者优先约束"起点"的约束属性;“多重约束”设置后面任务或者优先约束"终点"的属性:
图8.7 优先约束起点和终点
在优先约束编辑器,点击"求值运算"下拉菜单检查选项列表:
图8.8 "求值运算"列表
默认选项是约束,前几篇中我们所使用的选项。选择"约束"允许我们配置何时(或是否)执行下一个任务——纯粹依赖于前一个任务的执行结果。"值"下拉列表包含约束求值的选项:成功、失败和完成。
图8.9 "值"列表
更改"求值运算"为表达式,并在表达式文本框中键入@MyBool
图8.10 修改约束选项
点击测试按钮验证文本框中的表达式。返回结果应该如下所示:
图8.11 表达式测试返回结果
注意SSIS变量MyBool的值是一个Boolean的数据类型,它的默认值设为False.键入到表达式文本框的表达式必须是True或False(布尔值)。
我可以编辑表达式为"@MyBool == True".表达式"@MyBool"和"@MyBool == True"逻辑上是等价的,因为它们生成相同的结果。
一旦配置好,关闭优先约束编辑器。你的控制流应该如下所示:
图8.12 配置为表达式约束的控制流
在BIDS下执行Precedence包,会提醒你选择Script Task 1成功或失败:
图8.13 选择Script Task 1执行结果
无论你选择哪个选项,都不会影响后面的执行。因为优先约束求值纯粹依赖SSIS变量MyBool的值。@MyBool默认是False,因此Script Task 2永远不会执行,因为优先约束永远不会等于True.
为了创建一个更有用的测试,打开Script Task 1编辑器,点击ReadWriteVariables属性的省略号。打开选择变量窗口。选择User::MyBool变量:
图8.14 选择变量
点击确定关闭选择变量窗口。你的脚本任务编辑器的ReadWriteVariables属性现在应该包含SSIS变量User::MyBool
图8.15 添加用户变量后
点击编辑脚本按钮打开脚本编辑器窗口。用代码8.1编辑Public Sub Main()中的脚本:
Public Sub Main()
Dim sTaskName As String = Dts.Variables("TaskName").Value.ToString
Dim iResponse As Integer = MsgBox("Set MyBool to True?", MsgBoxStyle.YesNo, sTaskName)
If iResponse = MsgBoxResult.Yes Then
Dts.Variables("User::MyBool").Value = True
Else
Dts.Variables("User::MyBool").Value = False
End If Dts.TaskResult = ScriptResults.Success
End Sub
代码8.1
关闭脚本编辑器然后点击确定关闭脚本任务编辑器。在BIDS调试器下执行Precedence包:
图8.16
如果你点击"是"按钮,你会看到消息框显示Script Task 2执行完成:
图8.17
如果你点击"否"按钮,Script Task 2将不会执行:
图8.18
右击Script Task 2点击复制,然后在控制流的空白处右击选择粘贴。一个新的脚本任务添加到控制流,名称是"Script Task 2 1",重命名新脚本任务为"Script Task 3",并从Script Task 1连接一个新优先约束:
图8.19
如果你你现在在BIDS调试器下执行包,不管你将变量MyBool的值设置为True还是False,Script Task 1都会成功执行。当它成功时,Script Task 1与Script Task 3之间的优先约束求值为True,然后会显示Script Task 3完成的消息框:
图8.20
让我们编辑Script Task 1与Script Task 3之间的优先约束。右击优先约束打开优先约束编辑器,修改求值运算为"表达式",在表达式文本框键入"!@MyBool"(不需要双引号):
图8.21
"!@MyBool"表达式可以理解为"Not MyBool"——如果SSIS变量User::MyBool的值是False那么!@MyBool就是True.点击确定按钮关闭优先约束编辑器。你的控制流应该如下图所示:
图8.22
不知道你那边如何,但是我这边看起来有点凌乱。函数图标在一起了。我们可以适当调整优先约束的位置,让整体看起来明朗些:
图8.23
在BIDS调试器下执行包。如果Script Task 1的提示选择"是",Script Task 2将会执行:
图8.24
如果Script Task 1的提示选择"否",Script Task 3将会执行:
图8.25
优先约束提供巨大的视觉反馈。比如,蓝色表示一个求值运算设置为完成。但是我们的求值运算完全忽略了约束。让我们思考一下,约束的求值有:成功、失败和完成。成功意味着之前的任务成功;失败意味着之前的任务失败。成功和失败都表明完成。完成表明之前的任务已经操作但不管它是成功还是失败的。完成忽略之前任务执行的结果,不管它是成功还是失败。
当我将求值运算设置为表达式时同样会忽略之前任务执行折结果。我只关心任务是否完成。
复制禁用保存已有的任务
在我们继续前行之前,让我们先保存已经操作过的工作。从工具箱拖一个序列窗口到控制流:
图8.26
如果你在控制流的空白处点击一下,然后画/拖出一个方框覆盖三个脚本任务,你就可以把它们当做一个组来拖动它们到序列容器:
图8.27 将脚本任务拖放到序列容器
当你这样操作的时候,大多数情况下序列容器会自动调整大小以包括拖入里面的组件。如果它没有自动调整,手动调整以免引起误解。
右击序列容器点击复制。右击控制流的空白处点击粘贴。你的控制流应该如下所示:
图8.28 复制并粘贴序列容器
现在右击序列容器(原始那个)点击禁用。序列容器和它里面的组件都会被禁用(显示为灰色):
图8.29 禁用序列容器
现在我们就可以继续做新工作的时候保存已经完成的工作了。
多重约束
在序列容器1中删除Script Task 1.这也会删除Script Task 1与Script Task 2之间的约束。复制Script Task 2然后粘贴到序列容器1,并重命名为Script Task 4.移动Script Task 2让它和Script Task 4并排,然后连接Script Task 2和Script Task 3之间的优先约束;在Script Task 4与Script Task 3之间创建一个新的约束。
图8.30 创建Script Task 4并连接约束
现在,让我问你,为了让Script Task 3执行需要有什么操作?
1、Nothing.当其他任务执行后Script Task 3就会执行
2、当Script Task 4或Script Task 2执行并成功后Script Task 3就会执行
3、当Script Task 4与Script Task 2执行并成功后Script Task 3就会执行
4、Script Task 3永远不会执行
答案可以从优先约束编辑器的多重约束找到:
图8.31 多重约束"逻辑与"
记住,多重约束定义优先约束在终点如何操作。当终点任务只有一个优先约束限制时,这些选项是没有实际意义的。但是当有两个及以上约束在终点任务,这个选项就很重要。
默认是选择"逻辑与",这意味着在终点任务的所有约束必须都通过求值运算,才能执行后面的任务。本例中,Script Task 4与Script Task 2必须执行并成功后Script Task 3才会执行。让我们在BIDS调试器下执行包以验证前面的推测:
图8.32
直到Script Task 4与Script Task 2执行并成功后,Script Task 3才会开始执行(与上面的第3种情况相符)。这就是多重约束的"逻辑与"操作。接下来让我们测试"逻辑或"。停止调试,双击打开打开优先约束编辑器。更改多重约束为"逻辑或",如下图所示:
图8.33 多重约束"逻辑或"
点击确定按钮关闭优先约束编辑器。你可能注意到连接到Script Task 3的优先级约束的外观上的变化。优先约束现在都用虚线表示,如图8.34所示:
图8.34
因为多重约束配置处理终点任务,修改其中一个会影响到所有连接到终点任务的约束。
之前,在我们配置多重约束为"逻辑与"时,所有的优先约束需要先求值,之后的任务才会执行。我们现在配置为"逻辑或",让我们测试下,为了让Script Task 3执行需要有什么操作?在BIDS调试器下执行包,此时你的控制流应该类似于图8.35:
图8.35
只要Script Task 4或Script Task 2执行成功,Script Task 3就会开始执行。
混合约束和表达式运算
停止调试,双击打开Script Task 4和Script Task 3之间的优先约束编辑器。更改多重约束选项为"逻辑与"。设置求值运算为"表达式和约束",确保值设置为成功,表达式设置为@MyBool:
图8.36
为了这个约束求值,之前的任务必须执行且成功,并且SSIS变量User::MyBool必须为True.让我们查看变量,如图8.37,MyBool的值设置为Flase
图8.37
这个约束的值会为True吗?不会。因为约束(成功)的值必须为True而且表达式(MyBool)的值也必须为True.但是MyBool是Flase.因此不管Script Task 4成功或失败,MyBool是Flase会阻止约束进行求值。
测试一下。在BIDS调试器下执行包并观察结果。你永远不会看到包执行到Script Task 3:
图8.38
停止调试。让我们再次打开Script Task 4和Script Task 3之间的优先约束编辑器。修改求值运算为"表达式或约束":
图8.39
关闭优先约束编辑器,然后执行包。这一次,Script Task 3将会执行:
图8.40
因为连接到Script Task 3的优先约束的值都为True.很容易理解Script Task 2和Script Task 3之间的约束的值。但是Script Task 4和Script Task 3之间的约束是如何求值的呢?求值运算设置为"表达式或约束"意味着只要表达式或约束的值为True即可。如果两者都为True,约束还是为True.但是或条件要求至少有一个(表达式或约束)值为True.这里表达式MyBool是Flase;约束的值是True(之前的任务成功执行)。这就是为什么Script Task 3会执行。
优先约束注释
你可能已经注意一件事:当我们在求值运算中做最后一次更改时(从"表达式和约束"改为"表达式或约束"),控制流中的图标没有任何可视化的改变。有一种方法:点击选择优先约束,然后按F4打开属性窗口。把第一个属性ShowAnnotation的值设置为"ConstraintOptions":
图8.41
ShowAnnotation属性的ConstraintOptions会显示求值运算的说明:
图8.42
强化深入
首先,让我们编辑Script Task 2和Script Task 4中的Public Sub Main()如下所示:
Public Sub Main()
Dim sTaskName As String = Dts.Variables("TaskName").Value.ToString
Dim iResponse As Integer
iResponse = MsgBox("Succeed " & sTaskName & "?", _
MsgBoxStyle.YesNo + MsgBoxStyle.Question, _
sTaskName & " Success Question")
If iResponse = vbYes Then
Dts.TaskResult = ScriptResults.Success
Else
Dts.TaskResult = ScriptResults.Failure
End If End Sub
代码8.2
代码为每一个脚本任务创建提示任务是否成功。编辑Script Task 4和Script Task 3之间的优先约束,设置求值运算为"表达式和约束",值设置为失败,表达式设置为@MyBool
图8.43
编辑Script Task 2和Script Task 3之间的优先约束,设置求值运算为"表达式",表达式设置为!@MyBool
图8.44
如果两个优先约束的ShowAnnotation属性都设置为ConstraintOptions,你的控制流看起来应该如图8.45所示:
图8.45
注意,将求值运算设置为表达式会引起ConstraintOptions显示为"完成和表达式"。正如文章的前面提到,这是符合逻辑操作的。
执行包,条件选择Script Task 4为失败(否),Script Task 2为成功(是):
图8.46
This is a trick question.Script Task 3永远不会执行。因为优先约束的多重约束设置为"逻辑与"。所有约束的值都必须为True才会允许Script Task 3执行。但是它们含有对SSIS变量MyBool的互斥条件:一个约束当MyBool为True时为真,另一个约束当MyBool为Flase时为真。MyBool不可能同时为True和Flase.如果存在互斥条件,你必须将相应的多重约束设置为"逻辑或"。
让我们编辑Script Task 2和Script Task 3之间的优先约束,修改表达式为@MyBool:
图8.47
修改SSIS变量MyBool的值为True:
图8.48
如果我在BIDS调试器执行包,条件选择Script Task 4为失败,Script Task 2为成功/失败;Script Task 3就会执行:
图8.49
一旦我们移除互斥条件,设置为逻辑与的多重约束就会为真,然后执行下面的任务。
Another "Gotcha"
之前我们禁用了序列容器。现在让我们禁用Script Task 4:
图8.50
当我们在BIDS调试器下执行包,Script Task 4不会执行,但是Script Task 3也不会执行:
图8.51
为什么?MyBool的值为True而且Script Task 2完成。Script Task 4直接跳过去了。当Script Task 4禁用后,控制流断言它是成功的。这就意味着失败优先约束不会进行求值。
总结
这一篇,我们使用SSIS变量控制优先约束的求值,检查多重约束,and took a look at a couple “gotchas” to avoid when configuring precedence constraints to control workflow in an SSIS package.