微软 BI 系列随笔 - SSIS 2012 高级应用 - Script Component处理不规则平面文件


场景介绍

在使用SSIS从平面文件导入源数据时,最常遇到的是以下两种情况:

  1. 导入规则的平面文件,这种文件的每一行数据的解析规则都是一样的
  2. 导入不规则的平面文件,这种文件可能包含多种数据结构,比如某些行是头(Head),某些行是内容(Content),某些行是尾(Tail),三种数据的解析格式都不一样
  3. 源文件的字符编码集不一样,例如文件是来自于不同的系统(或区域),这些文件的编码有可能是ANSI,Unicode或者UTF-8中的任何一种。

对于第一种情况,可以使用平面文件连接器(FlatFile Connection)来解析,通过配置他的字符集,格式,分隔符等就能做到。

对于第二种情况,要是使用平面文件连接器(FlatFile Connection)的话,需要使用临时文件,即使用平面文件连接器从源文件中中将每一行的数据读为一个类型,然后根据内容使用条件分离(Conditional Split)将内容分离,重新组织新的文件。然后正对新生成的文件做常规处理实现数据导入。

对于第三种情况,比较复杂,目前是不能使用平面文件连接器来解析的。平面文件连接器虽然支持表达式设置CodePage和是否是Unicode,以及其他的设置,但是具体操作起来比较麻烦。设计时有严重的干扰,经常常常导致编译不通过。

本文将针对第二种情况和第三种情况给读者一个思路,即使用Script Component作为数据源解析文件,输出数据,同时结合处理第二种情况。

实施步骤

分析平面文件格式

假设我们有如下格式的平面文件:

首先这个文件的编码是不规则的,他含有西班牙文(还有其他的文件,是不含这个文字集的)。

第二,他有02H和02D两种内容格式,姑且认为是Header和Detail。

所以说这种类型的文件使用平面文件连接器根本无法解析。

那么我们怎么办呢?看下面。

Script Component源

定义输入与输出

SSIS中支持使用Script Component作为数据源,你可以使用C#或者VB代码去做任何事情,然后将构造一个或多个数据集作为输出。如下所示:

本例中我定义了一个EDCOrder的输出用来放Header的内容,OrderDetail的内容我不需要所以没有定义。

我另外定义了一个FailedRows来放Header解析失败的的内容。没有输入。

编写脚本

在脚本页选择要使用的变量和脚本语言。

点击编辑脚本(Edit Script...)

这时候会打开一个Vsta project的脚本编辑项目。

这个项目包含三个文件

  • ComponentWrapper.cs - 包含变量类,连接类和用户控件类
  • BufferWrapper.cs - 定义了输出类
  • main.cs - 脚本文件的入口函数,继承自用户控件类。

main.cs这个文件有三个方法

  • PreExecute - 执行之前
  • PostExecute - 执行之后
  • CreateNewOutputRows 创建输出行

为了解析平面文件,我们定义了如下参数,主要是一个StreamReader来读文件,时间记录和行号:

    System.IO.StreamReader sr;
DateTime now;
int rowNumber,failedRows;
Guid fileId;
string ContentLengthError;
string CoposIdNullError;
string errorMsg;

在PreExecute中这么写,根据传进来的文件名打开文件,定义错误消息,初始化变量

    /// <summary>
/// This method is called once, before rows begin to be processed in the data flow.
///
/// You can remove this method if you don't need to do anything here.
/// </summary>
public override void PreExecute()
{
base.PreExecute(); now = DateTime.Now;
rowNumber = ;
failedRows = ;
fileId = Guid.Parse(Variables.pvFileID.ToString());
ContentLengthError = "Line content validation failed. Line content length is not equal to 1141.";
CoposIdNullError = "Line content validation failed. Copos id is required.";
errorMsg = "Filename: " + Variables.pvLoadFile + System.Environment.NewLine;
sr = new System.IO.StreamReader(Variables.pvLoadFile);
}

在PostExecute中这么写,关闭文件,返回错误信息给脚本调用的变量

    /// <summary>
/// This method is called after all the rows have passed through this component.
///
/// You can delete this method if you don't need to do anything here.
/// </summary>
public override void PostExecute()
{
base.PostExecute(); sr.Close();
Variables.pvFailedRows = failedRows;
if (errorMsg != "Filename: " + Variables.pvLoadFile + System.Environment.NewLine)
Variables.pvErrorMessage += System.Environment.NewLine + errorMsg;
}

在CreateNewOutputRows这么写,根据每一行的内容判断消息类型,验证消息内容,生成输出行。

    public override void CreateNewOutputRows()
{
/*
Add rows by calling the AddRow method on the member variable named "<Output Name>Buffer".
For example, call MyOutputBuffer.AddRow() if your output was named "MyOutput".
*/
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
if (line.StartsWith("02H"))
{
if (line.Length != )
{
FailedRowsBuffer.AddRow();
FailedRowsBuffer.RowNumber = rowNumber;
FailedRowsBuffer.ErrorDescription = ContentLengthError;
FailedRowsBuffer.Content = line;
failedRows++;
errorMsg += "At Row: " + rowNumber.ToString() + System.Environment.NewLine + "Error Description: " + ContentLengthError + System.Environment.NewLine;
}
else
{
if (!string.IsNullOrEmpty(line.Substring(, )) && !string.IsNullOrWhiteSpace(line.Substring(, ))) //Coposid is required
{
EDCOrderBuffer.AddRow();
EDCOrderBuffer.Coposid = line.Substring(, );
EDCOrderBuffer.Custname = line.Substring(, );
EDCOrderBuffer.Custtel = line.Substring(, );
EDCOrderBuffer.Dlvytitle = line.Substring(, );
EDCOrderBuffer.Dlvyinitial = line.Substring(, );
EDCOrderBuffer.Dlvyname = line.Substring(, );
EDCOrderBuffer.Dlvyhouse = line.Substring(, );
EDCOrderBuffer.Dlvytel = line.Substring(, );
EDCOrderBuffer.Dsptchsrvc = line.Substring(, );
EDCOrderBuffer.Dlvydate = line.Substring(, );
EDCOrderBuffer.Dmworder = line.Substring(, );
EDCOrderBuffer.Dsptchemail = line.Substring(, );
EDCOrderBuffer.OrderDate = line.Substring(, );
EDCOrderBuffer.FileId = fileId;
EDCOrderBuffer.CreatedOn = now;
}
else
{
FailedRowsBuffer.AddRow();
FailedRowsBuffer.RowNumber = rowNumber;
FailedRowsBuffer.ErrorDescription = CoposIdNullError;
FailedRowsBuffer.Content = line;
failedRows++;
errorMsg += "At Row: " + rowNumber.ToString() + System.Environment.NewLine + "Error Description: " + CoposIdNullError + System.Environment.NewLine;
}
}
}
rowNumber++;
}
}

编译下下这个Vsta项目。

处理Script Component输出

这样,一个不规则的文件就被我们使用Script Component轻松的处理了。

  • 由于使用StreamReader,使用默认编码格式他会自动识别文件编码,避免了使用平面文件解析器出现乱码的情况
  • 由于针对文件内容分别处理,可以解析不规则文件,产生多个输出
  • 由于加入了自定义验证信息,可以验证消息的内容

疑问:

  • 使用Script Component能否平行执行,EDCOrderBuffer.AddRow()的时候是不是就已经产生了一行输出了呢?这个有待研究
  • 要写代码解析文件,有没有可以长度类型都是可以配置的,如果需要格式转换是否方便,或者在外部再做转换呢?

将这些问题留给读者朋友们去思考吧。

也可以给我留言大家一起讨论哦。

使用Script Component源处理不规则平面文件的更多相关文章

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

    开篇介绍 这一篇文章是 微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧 的续篇,在上篇文章中介绍到了对于这种不规则文件输出的处理方式.比如下图中 ...

  2. 微软BI 之SSIS 系列 - 通过 ROW_NUMBER 或 Script Component 为数据流输出添加行号的方法

    开篇介绍 上午在天善回答看到这个问题 - SSIS 导出数据文件,能否在第一列增加一个行号,很快就帮助解决了,方法就是在 SQL 查询的时候加一个 ROW_NUMBER() 就可以了. 后来想起在两年 ...

  3. Script component 用法

    在SSIS中,可以使用C#编写脚本,这是十分激动人心的事,能够使用C#代码,使得Script Component无所不能. 第一部分:组件简介Script Component 有三种类型:Source ...

  4. Data Flow ->> Script Component

    和Control Flow中的Script Task非常类似,不同的是Script Component是Per-Row的执行类型.打个比方,在Script Component中加入两个Output的字 ...

  5. Script Component 引用package variable

    ScriptComponet 的变量分为两种类型,ReadOnly和ReadWrite,使用C#引用这两种类型的变量,有一点不同. 1,创建两个变量 2,将变量传递给script component ...

  6. Unity3d 拖拽脚本报错Can't add the script component "" because the script class cannot be found

    解决办法: ①报错原因:文件名与文件内容中的类名不相符. ②关闭360.鲁大师等防护软件,重新安装系统.

  7. 微软BI SSIS 2012 ETL 控件与案例精讲面试 200 问(SSIS 面试题,ETL 面试题)

    开篇介绍 本自测与面试题出自 微软BI SSIS 2012 ETL 控件与案例精讲 (http://www.hellobi.com/course/21) 课程,对于学完本课程的每一课时和阅读完相关辅助 ...

  8. 微软BI SSIS 2012 ETL 控件与案例精讲课程学习方式与面试准备详解

    开篇介绍 微软BI SSIS 2012 ETL 控件与案例精讲 (http://www.hellobi.com/course/21) 课程从2014年9月开始准备,到2014年12月在 天善BI学院  ...

  9. 微软BI SSIS 2012 辅助阅读博客

    大家可以根据对应的视频课程名称查找相关的辅助阅读博客,有少量辅助阅读博客和视频课程讲解内容相同,大部分都是拓展总结部分.希望大家在学完每一个视频课程之后看看相关博客内容,这样可以在知识面和深度上继续得 ...

随机推荐

  1. python语言技巧

    一 在写之前 最好指定python的路径: #!/usr/bin/python python 在linux中需要添加编码方式:以免出现中文乱码 # -*- coding: UTF-8 –*-   二 ...

  2. 在python中使用图形库matplotlib

    matplotlib is a python 2D plotting library which produces publication quality figures in a variety o ...

  3. Eclipse 常用用法

    搞了很多年的.NET,  最近开始搞Eclipse.    刚开始使用 Eclipse,  发现  Eclipser 远没有Visual Stuido  强大.  所以先把Eclipse常用的用法总结 ...

  4. iOS开发网络篇—监测网络状态

    iOS开发网络篇—监测网络状态 一.说明 在网络应用中,需要对用户设备的网络状态进行实时监控,有两个目的: (1)让用户了解自己的网络状态,防止一些误会(比如怪应用无能) (2)根据用户的网络状态进行 ...

  5. python语法------时间函数

    1.导入函数库: import time 获取格式化的时间 你可以根据需求选取各种格式,但是最简单的获取可读的时间模式的函数是asctime(): #!/usr/bin/python # -*- co ...

  6. Josephu--Java链表实现

    public class Josephu { public static void main(String[] args) { Cyclink cyclink=new Cyclink(); cycli ...

  7. 快速分析apk工具aapt的使用

    前面walfred已经介绍了使用apktool对apk进行逆向编译,通过apktool我们的确可以反编译已经序列化后的AndroidManifest.xml和资源文件等等,但是有没有一种快速有效的工具 ...

  8. 理解RESTful架构(转载)

    本文转载自:http://www.ruanyifeng.com/blog/2011/09/restful.html 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软 ...

  9. android 缓存Bitmap 使用内存缓存

    private LruCache<String, Bitmap> mMemoryCache; /** * 判断内存大小 设置位图的缓存空间 */ private void judgeMem ...

  10. 关于oracle的rowid

    oracle数据库中表的每一行(元组)均有一个rowid,它是数据的详细地址,通过rowid,oracle可以快速的定位某行具体的数据的位置. ROWID可以分为物理rowid和逻辑rowid两种.普 ...