微软BI 之SSIS 系列 - Execute SQL Task 中的 Single Row 与 Full Result Set 的处理技巧

开篇介绍

Execute SQL Task 这个控件在微软BI ETL 项目中使用的频率还是非常高的,也是大部分入门 SSIS 初学者最早接触到的几个控制流控件。

我们通常使用 Execute SQL Task 的场景包含但不止于以下几类:

  1. 在从源端加载数据到 Staging 表之前使用 Execute SQL Task 执行一些 Truncate 操作。
  2. 执行一些 Log 的插入,更新操作。
  3. ETL 过程中的 Merge 语句操作。
  4. XML 的输出处理。

关于如何使用 Execute SQL Task 就不在本文讲解了,包括参数传递 Input,存储过程返回值 Output,包括 Return Value ,还有 SQL Source Type 等不同方式以及 OLD DB,ADO.NET 区别等基础概念文本也不会涉及到。

本文要讲解的内容是针对 ResultSet 的几种类型:None, Single row, Full result set, XML。其中 None 不用讲解,关于 XML 的内容大家可以参考我的另一篇文章 两种将 SQL Server 数据库数据输出成 XML 文件的方法,因此在这里只讲解 Single Row 和 Full Result Set 的处理方法。

Single Row

首先,Single Row 指的就是在 Execute SQL Task 中返回的就是一个单行的记录,允许多列。

通常在 Execute SQL Task 中使用 Single Row 的时候是为了在控制流中控制流程的流转,满足一定的条件走一个分支流程,不满足则走另外一个流程。

比如,在包执行之初查询数据库检查一下包的某些状态,或者数据的某些状态,只有在满足达到一定条件下才能执行剩下的流程,否则则不执行包或者报错。这类设计在各种不同的BI项目中可能都存在,那么其原型就是利用 Execute SQL Task 中的 Single Row返回的值来控制流程。

有以下几个步骤:

  1. SQL Statement 中的语句返回单行记录,可以多列。
  2. 需要在包中实现定义变量用来接收 Single Row 的某列上的值。
  3. 使用 Precedence Constraint 和变量来控制流程。

Single Row 案例

一条简单的查询语句返回一个单行双列的记录。

设置好连接并放好SQL语句,选择 Single Row。

添加三个变量

  1. EXISTING_COUNT - 用来接收查询中的返回的COUNT数。
  2. MAX_DATE - 用来接收查询中返回的最大时间。
  3. TEST_DATE - 测试时间,比如说 2014-01-01。

0 表示 Single Row 中的第一列,1 表示 Single Row 中的第二列。

下面放两个空的 Data Flow Task 并关联到 Execute SQL Task,点击连接线设置条件控制。

DFT_TEST_AFTER_MAX 上的连接线条件判断为:DATEDIFF( "dd", (DT_DBDATE) (@[User::TEST_DATE]) , (DT_DBDATE) (@[User::MAX_DATE])) >=0

DFT_TEST_BEFORE_MAX上的判断表达式为:DATEDIFF( "dd", (DT_DBDATE) (@[User::TEST_DATE]) , (DT_DBDATE) (@[User::MAX_DATE])) <0

左右两个数据流下各加入一个 Script Task 显示一下这几个变量的值,代码如下:

public void Main()
  {
   // TODO: Add your code here
            //User::EXISTING_COUNT,User::MAX_DATE,User::TEST_DATE
            string existing_count = Dts.Variables["User::EXISTING_COUNT"].Value.ToString();
            string max_date = Dts.Variables["User::MAX_DATE"].Value.ToString();
            string test_date = Dts.Variables["User::TEST_DATE"].Value.ToString();

            MessageBox.Show("Existing Count - " + existing_count + ", test date - " + test_date + ", max date - " + max_date);
   Dts.TaskResult = (int)ScriptResults.Success;
  }

保存并执行包,流程走了右边,因为 MAX DATE 要比 TEST DATE 大。

修改 TEST_DATE,那么 TEST DATE 要比 MAX DATE 大。

这就是 Execute SQL Task 中 Single Row 的使用方法。

FULL Result Set

通常在 Execute SQL Task 中使用到 FULL Result Set 就一定会结合 Foreach Loop 来使用,所适用的场景是循环便利查询结果集中的每一行数据,将每一行的数据其中某几列取出来放入到变量中,然后同样在 Foreach Loop 中的其它控制流控件使用这些变量做一些操作。

疑问

像这种遍历为什么不放到数据库中直接遍历不是更好吗?问题就在于,有时操作的对象并不仅仅是数据库中的表对象,而是涉及到不同的文件处理,这时就需要使用这种方式了。比如,我的目标数据源中有几百个文件,且文件的命名包括产品的名称,现在表中有产品名称的记录。需要循环遍历表中的产品,并对比哪些文件命名包含有这些产品名称,包含产品名称的文件则抽取数据,不包含产品名称的文件则移到其它目录。

下面使用这个查询作为一个示例结果集 -

使用 FULL RESULT SET

定义如下几个变量 - ORDER_SET 用来保存 Execute SQL Task 查询中的结果集,其它变量用来在循环每一行的时候保存每一列的值。注意:返回的结果集要使用 OBJECT 类型的变量来保存,这个 OBJECT 类型在内部以集合的形式存在并保存整个查询结果集。

EXECUTE SQL TASK 的 Mapping。

Foreach 下的设置

我们可以通过 Foreach 的方式循环遍历这个返回的集合。添加一个 Foreach 控件,并设置遍历方式 - Foreach ADO Enumerator 和要遍历的集合对象。

遍历这个集合的时候,每次返回一行,这一行也是一个集合,通过设置索引0,1,2 将这个集合的元素赋值给各个变量。

在 Foreach 控件中添加一个 Script Task 用来显示每一行中各列的内容。

Script 中的脚本代码如下:

public void Main()
  {
   // TODO: Add your code here
            //User::PRODUCT_ID,User::SALES_ORDER_DETAIL_ID,User::UNIT_PRICE
            string detailID = Dts.Variables["User::SALES_ORDER_DETAIL_ID"].Value.ToString();
            string productID = Dts.Variables["User::PRODUCT_ID"].Value.ToString();
            string unitPrice = Dts.Variables["User::UNIT_PRICE"].Value.ToString();

            MessageBox.Show("Detail ID - " + detailID+", Product ID - "+productID +", Unite Price - "+ unitPrice);
   Dts.TaskResult = (int)ScriptResults.Success;
  }

保存并执行包,第一次循环的结果是失败的,但是可以看出来我们的结果集返回是没有问题的。

出错的原因如下:

Error: The type of the value (String) being assigned to variable "User::SALES_ORDER_DETAIL_ID" differs from the current variable type (Int32). Variables may not change type during execution. Variable types are strict, except for variables of type Object.

Error: The type of the value (String) being assigned to variable "User::PRODUCT_ID" differs from the current variable type (Int32). Variables may not change type during execution. Variable types are strict, except for variables of type Object.

Error: The type of the value (String) being assigned to variable "User::UNIT_PRICE" differs from the current variable type (Decimal). Variables may not change type during execution. Variable types are strict, except for variables of type Object.

这是由于在集合中的数据默认都是 String 类型的,是不能够直接转换成我们定义的变量所指定的类型,因此需要修改我们的变量类型。

再次执行就可以看到遍历的效果,第一次 -

最后一次 -

Script Task 中的循环遍历

同样的 Object 对象,不仅仅可以在 Foreach 中循环遍历,也可以直接在 Script Task 中循环遍历。

添加一个 Script Task,把结果集对象放入变量列表中。

脚本代码如下 -

using System.Data.OleDb;

public void Main()
  {
   // TODO: Add your code here
            OleDbDataAdapter adapter = new OleDbDataAdapter();
            DataTable dataTable = new DataTable(); 

            adapter.Fill(dataTable, Dts.Variables["User::ORDER_SET"].Value);

            foreach (DataRow row in dataTable.Rows)
            {
                MessageBox.Show(row[0] + "," + row[1] + "," + row[0]);
            } 

   Dts.TaskResult = (int)ScriptResults.Success;
  }

一样可以实现遍历的效果 -

最后一条记录-

总结

通过讲解 Execute SQL Task 的两种查询结果返回方式 Single Row 和 Full Result Set,实际上由此引申出了何时使用 Single Row 以及 Full Result Set 的场景。熟悉和掌握这些场景可以帮助我们在复杂的 ETL 项目中找出各种不同的解决方案,可以非常灵活的解决一些实际问题。

再次,特别通过 Foreach Loop Container 以及 Script Task 学习到了两种解析 Full Result Set 的方式,其中涉及到的知识点以及细节还是比较多的,特意总结下来希望可以帮助到大家。

更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)  如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。

时间: 2024-10-21 08:43:45

微软BI 之SSIS 系列 - Execute SQL Task 中的 Single Row 与 Full Result Set 的处理技巧的相关文章

微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件

开篇介绍 大多数情况下我们的 SSIS 包都会配置在 SQL Agent Job 中周期性的按计划执行,比如每天晚上调用 SSIS 包刷新数据,处理 Cube 等.一旦 SSIS 包中出现任何异常,报错,那么配置在 SQL Agent Job 中的通知,邮件提醒就会把这些错误信息发邮件到指定的用户或者系统维护者,这样就起到了一个错误监控的作用. 但是在有的情况下,有一些自定义的 SSIS 调度框架的计划调度都不是通过 SQL Agent Job 配置来完成的.比如我以前在一个小项目中设计过一个

微软BI 之SSIS 系列 - 使用 SQL Profilling Task (数据探测) 检测数据源数据

开篇介绍 SQL Profilling Task 可能我们很多人都没有在 SSIS 中真正使用过,所以对于这个控件的用法可能也不太了解.那我们换一个讲法,假设我们有这样的一个需求 - 需要对数据库表中的一些数据做一些数据分析,比如统计一下数据表中各列中实际数据的长度,各长度区间范围:比如统计一下各数据列中非空字段的比例,表的行数,重复字段等等.那么如果不是专门做过这种数据源数据分析的话,可能不知道用什么方式能够非常快的得到这些信息.写 SQL 语句?我想这个过程也是非常耗费时间和精力的. 实际上

微软BI 之SSIS 系列 - 在 SQL 和 SSIS 中实现行转列的 PIVOT 透视操作

开篇介绍 记得笔者在 2006年左右刚开始学习 SQL Server 2000 的时候,遇到一个面试题就是行转列,列转行的操作,当时写了很长时间的 SQL 语句最终还是以失败而告终.后来即使能写出来,也是磕磕碰碰的,虽然很能锻炼 SQL 功底,每次都要挣扎一番,溺水的感觉.记得SQL Server 2005 以后就有了 PIVOT 和 UNPIVOT 这两个函数,可以非常方便的实现行转列和列传行的操作,就不再那么挣扎了.后来,在一个 08 项目中,有一位新的女同事在改一个 ETL,发现 SSIS

微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug

开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配置,还特意测试过这个细节,获取错误并理解了这个功能.但是现在回去再次测试 2008 的版本时,发现这个功能在 2008 中其实也是错误的,把我印象中的测试结果完全给推翻了,所以到现在已经搞不清楚我当时到底是如果得出这个错误的. 疑似功能 Bug 描述 在 SSIS 包中定义了用户自定义变量 - PV

微软BI 之SSIS 系列 - 在 SSIS 中导入 ACCESS 数据库中的数据

开篇介绍 来自 天善学院 一个学员的问题,如何在 SSIS 中导入 ACCESS 数据表中的数据. 在 SSIS 中导入 ACCESS 数据库数据 ACCESS 实际上是一个轻量级的桌面数据库,直接使用文件形式存储.在国内大量使用 ACCESS 作为 BI 数据源并不多,但是在国外特别是美国使用的还比较多,因为他们的 IT 基础起步比较早.在我的第一个美国的医疗保险项目中,就遇到过大量的 ACCESS 数据源,前后总共有 500 多个 ACCESS 表.而现在从国外一些朋友反馈的情况仍然还有在使

微软BI 之SSIS 系列 - 使用 Script Component Destination 和 ADO.NET 解析不规则文件并插入数据

开篇介绍 这一篇文章是 微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧 的续篇,在上篇文章中介绍到了对于这种不规则文件输出的处理方式.比如下图中的这种不规则文件,第一行,第二行 Header 部分,第三行的内容 Content 部分,最后一行的 Trailer 部分. 在前几个课程 微软BI SSIS 2012 ETL 控件与案例精讲 第43,44,45,46 课中,我分别讲解了如何使用 .Script Component Source 解

微软BI 之SSIS 系列 - Lookup 中的字符串比较大小写处理 Case Sensitive or Insensitive

前几天碰到这样的一个问题,在 Lookup 中如何设置大小写不敏感比较,即如何在 Lookup 中的字符串比较时不区分大小写? 实际上就这个问题已经有很多人提给微软了,但是得到的结果就是 Closed and Won’t fix. 说白了,这个就是 By Design,包括到现在的 2012 也没有这个配置选项. https://connect.microsoft.com/SQLServer/feedback/details/339069/ssis-case-sensitive-data-flo

微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧

案例背景与需求介绍 之前做过一个美国的医疗保险的项目,保险提供商有大量的文件需要发送给比如像银行,医疗协会,第三方服务商等.比如像与银行交互的 ACH 文件,传送给协会的 ACH Credit 等文件.这些文件格式在美国都是开放的,通用的,可以直接到相关网站下载.也就是说像银行,协会等他们接受这种固定格式的文件,读取数据,读取公司编号进行业务来往或者记录.我当时就是直接在网上搜索到一个 PDF 格式的文件说明,大概有10来页,就是告诉你这个格式是如何定义,应该如何来处理的. 那么这种文件并非像我

微软BI 之SSIS 系列 - 对于平面文件中 NULL 值处理过程中容易极易混淆的几个细节

最近有人问我 OLE DB Destination 中的 Keep Nulls 如何控制 NULL 值的显示,为什么选中了 Keep Nulls 但是数据库中没有 NULL 值? 为什么在 Flat File Source 中勾选上了 Retain null values from the source as null values in the data flow 但是为什么目标表上显示的是一个当前日期,而不是 NULL 值等等,单开此文来解释这些非常容易混淆的概念. 在比较纯粹的 ETL 项