zoukankan      html  css  js  c++  java
  • 使用Script Component源处理不规则平面文件

    微软 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 = 1;
            failedRows = 0;
            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 != 1141)
                    {
                        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(3, 30)) && !string.IsNullOrWhiteSpace(line.Substring(3, 30))) //Coposid is required
                        {
                            EDCOrderBuffer.AddRow();
                            EDCOrderBuffer.Coposid = line.Substring(3, 30);
                            EDCOrderBuffer.Custname = line.Substring(33, 64);
                            EDCOrderBuffer.Custtel = line.Substring(257, 20);
                            EDCOrderBuffer.Dlvytitle = line.Substring(287, 4);
                            EDCOrderBuffer.Dlvyinitial = line.Substring(291, 1);
                            EDCOrderBuffer.Dlvyname = line.Substring(292, 30);
                            EDCOrderBuffer.Dlvyhouse = line.Substring(322, 30);
                            EDCOrderBuffer.Dlvytel = line.Substring(482, 20);
                            EDCOrderBuffer.Dsptchsrvc = line.Substring(882, 9);
                            EDCOrderBuffer.Dlvydate = line.Substring(891, 8);
                            EDCOrderBuffer.Dmworder = line.Substring(959, 30);
                            EDCOrderBuffer.Dsptchemail = line.Substring(989, 70);
                            EDCOrderBuffer.OrderDate = line.Substring(1059, 12);
                            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()的时候是不是就已经产生了一行输出了呢?这个有待研究
    • 要写代码解析文件,有没有可以长度类型都是可以配置的,如果需要格式转换是否方便,或者在外部再做转换呢?

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

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

  • 相关阅读:
    个人心情闲扯贴~~
    近阶段学习感悟--大一下半学期
    HDU 1003 Max Sum 解题报告
    开始我的新园地--献给我的那些学长们
    软件公司职位简称
    Sql Server参数化查询之where in和like实现详解 [转]
    21个值得收藏的Javascript技巧
    [转]js刷新父窗体
    Oracle10g 连接 sqlserver 在server2008r2 中连接 iis7 .net4.0
    Oracle10g 连接 sqlserver hsodbc dblink 方式 非透明网关
  • 原文地址:https://www.cnblogs.com/JasonLiao/p/SSISScriptComponentSource.html
Copyright © 2011-2022 走看看