2006 年 12 月 04 日
本
文着重介绍了如何使用 DataStage BASIC 语言开发一个用户自定义的功能函数,并且以一个完整的 Server Job
实例为读者讲解在 Transformer Stage
中如何使用内置的和自定义的函数来转化数据。文章的最后介绍了如何重用自定义的功能函数。读者定位为具有一定 DataStage 使用经验的开发人员。
DataStage 概述
IBM WebSphere
DataStage是一个图形化的进行数据整合的开发环境,可以用来实现数据抽取,转化,净化,加载到目标数据库或者数据仓库中,
即ETL过程(Extract, Transform, Cleansing,
Load)。DataStage使用Stage实现对数据的操作。在整个操作数据的过程中,需要创建从不同的数据源抽取数据的Stage,以及用来转化和
净化数据的Stage,还需要一些Stage将数据加载到目标数据库中,一个ETL
job就是一些被连线连接在一起的Stages,数据则是从一个Stage流向下一个Stage。关于DataStage的基本使用方法,读者可以参考发
表在developWorks中国网站上的《用 IBM WebSphere DataStage 进行数据整合》系列文章。
Transformer Stage 介绍
在数据整合的整个过程中,很重要的一步就是对抽取数据的格式或者内容进行必要的转化。用户可以在Transformer Stage中,对传入的数据进行任何必要的处理,再把处理好的数据传给下一个Stage。
图1就是一个正在被编辑的Transformer Stage,窗口的上半部分显示了输入与输出的字段之间的对应关系,其中DSLink13是输入的连线名称,DSLink4是输出的连线名称。而窗口下半部分详细说明了每一个输入或者输出字段的定义。
图1.一个Transformer Stage的实例
如果仅仅是把输入与输出的字段对应起来,那并不能体现出Transformer
Stage的作用。实际上,用户可以通过编辑输出字段的derivation区域的值,对输入字段的值进行修改,使其结果可以满足输出字段的要求。用户只
需要双击某个字段的derivation区域(图1中的红色区域)就可以对其进行编辑。这时,再点击右键即可出现一个菜单以便用户选择DataStage
内置的或者用户自定义的功能函数的分类。当用户选择了其中的一种类型,就会弹出另一个窗口展示出DataStage中此种类型中的所有内容,如图2所示。
图2. DataStage中各种用于数据转化的功能函数
用户选中某一个函数之后,再把输入字段的名称作为这个函数的输入变量,就可以完成这个编辑的任务。图3就是编辑好的结果。
图3. 编辑后的输出字段的derivation区域
此处选用的DIGITS(Arg1)函数的功能是,去除掉输入字符串中包含的数字,只返回字符串中的所有字符。譬如DIGITS("abc123def") 的返回结果为"abcdef "。
DataStage BASIC语言
开发人员一般都会有过这样的经历:一个功能强大的软件往往内置了丰富的功能函数,可以满足用户大部分的需要。但是在实际应用中,用户还是需要根据实
际需求开发一些自定义的功能函数,这也就要求软件提供相应的方法使得用户能够进行二次开发和定制,从而扩展平台的功能。DataStage不仅有很多内置
的功能函数,而且提供了相应的机制,可以让用户自己开发所需功能的函数。
在DataStage中,大多数自定义的函数需要使用DataStage BASIC语言进行开发。DataStage
BASIC语言是一种可以运行在DataStage平台上,功能强大的面向过程的开发语言。而且,这种语言对于初学者很容易入门,并能满足经验丰富的开发
人员的开发需要。值得注意的是,尽管很多编程语言在定义变量的时候就严格区分了变量的类型,但是在DataStage
BASIC中情况却有所不同。DataStage BASIC是一种弱类型(weak
typing)的编程语言,一个变量在定义时不必声明其类型,而是统一被当作字符串保存起来,也不会因此而发生编译错误。只有在程序的执行期,
DataStage才根据上下文关系来确定变量的类型
下面将用一个实际开发的例子,给读者介绍一下如何使用DataStage BASIC语言开发一个自定义的功能函数。本文中如果没有特殊说明,当提到BASIC语言时,均是指DataStage BASIC语言。
本文中自定义函数的功能是这样的:
这个函数有三个输入的变量OriStr, Delmt, BitValue和一个输出变量Ans。
其中OriStr的值是一个字符串,其中包含了由分隔符分隔开的若干个子串。比如:"light_01,light_02,light_03"就是由分号分隔的三个子串组成的。
Delmt是用户指定的分隔符,其缺省值为分号,也可以是任意字符。
BitValue的值是一个非负的整数。
Ans的值也是一个字符串。
整个函数的流程是这样的,首先对输入变量OriStr和BitValue进行验证。如果它们的值不满足要求,则离开函数;如果满足要求,就把
BitValue转化为二进制的数。这个二进制的数的每一位分别对应于OriStr中的每个子串。当子串所对应的数值为"1"时,这个子串就被添加到输出
字符串中。反之,那些对应的数值为"0"的子串就不会出现在输入字符串中。那些被选出来的子串由逗号分隔,组成了输出字符串。
举例来说,当OriStr ="light_01,light_02,light_03",BitValue = 6的时候,把BitValue转化为二进制的数"110",把这个二进制数的每一位和每个子串的一一对应起来,如下表所示。
每个子串 |
light_01 |
light_02 |
light_03 |
BitValue每一位的值 |
1 |
1 |
0 |
前两个子串"light_01"和"light_02"所对应的值都是"1",所以被添加到输出字符串中;而第三个子串"light_03"对应的值为"0",就不会被添加到输出字符串中。因此,这个例子的结果就是"light_01,light_02"。
具体的开发步骤如下:
1. 打开DataStage Designer或者DataStage Manager,在左边的Repository View(图4)中用户可以看到保存在DataStage Server中所有资源。
图4. Repository View
2. 选中Routines,然后点击右键选择New Category。在弹出的窗口中填写Customized,之后点击确定。这样就创建了一个新的分类,后面新建的routine就保存在这个分类之中。
3. 再次选中Routines,然后点击右键选择New Server Routine。在Server
Routine的编辑窗口中,在Routine Name一栏中填写自定义函数的名字,在Type下拉框中选择Transform
Function,在Category一项中选择刚创建的Customized。Short Description和Long
Description分别是函数功能的简单和具体描述,内容可以介绍每个输入变量的含义,也可以使用具体的例子为用户展示函数功能。图5就是内容填写完
成的General标签页面。
图5. New Server Routine编辑窗口中General标签页面
4.在Creator标签页填写作者和公司名称等版权信息,如图6所示。
图6. New Server Routine编辑窗口中Creator标签页面
5. 在Arguments标签页中定义这个函数的输入变量的名称,以及对每个变量的描述,如图7所示。
图7. New Server Routine编辑窗口中Arguments标签页面
6.Code标签页,就是真正编写这个函数的BASIC代码的地方。
图8. New Server Routine编辑窗口中Code标签页面
该函数的完整代码可以在参考资料中找到,下载后读者可以使用各种常用的文本编辑器打开阅读。
由于篇幅有限,下面针对这段BASIC程序中使用到的一些语法做一下解释说明。本文中使用到的是BASIC语言中最基本最经常使用的语法知识,更多的语法和函数功能介绍请阅读参考资料。
(1). BASIC语言有四种符号用来标记注释,分别是REM,*,! ,$*。例如,
*Initial. Validate the input parameters.
这一行以"*"星号开头,就表示这一行内容是程序的注释。
(2). 条件判断语句,其语法结构如下:
If condition
Then statements
End
[Else statements
End]
|
其中condition可以是一个数字,也可以是一个比较关系式。当条件为真时,程序执行Then后面的表达式;当条件为假时,则会执行Else后面的表达式。在Basic语言中,逻辑关系运算符是这样的:
(3). 使用BASIC自带的一些功能函数。
Oconv(expression, conversion)
其中expression表示一个字符串,conversion则表示这个字符串的输出格式。
Oconv("10", "MB")的功能是把十进制数"10",转化为二进制数"1010";
而 Oconv("10", "MD1")的功能是把十进制数"10"的小数点左移一位,转化为"1.0"。
DSLogInfo (Message, CallingProgName)和DSLogWarn (Message,
CallingProgName) 的功能十分相似,都是在执行Server
Job的时候,输入一些必要的日志信息,帮助用户了解程序的执行情况。其中Message为输出的日志信息的内容,CallingProgName就是调
用这个函数的程序名。区别在于DSLogInfo输出的仅仅是普通的日志信息,而DSLogWarn则是输出一些警告的信息给用户。
Ans="OriStr is blank, maybe there is no lights in this room"
Call DSLogWarn(Ans,"TransBitMask")
Call DSLogInfo("OriStr:" : OriStr : " is valid","TransBitMask")
|
(4). 两种循环条件语句
a). For…Next语句,其基本语法如下:
For variable = start To end [Step increment]
[loop.statements]
Next [variable]
|
其中start To end定义了变量变化的区间;而increment定义了每次变量增加的值,当start的值大于end的值,increment的值可以是负数。
For num = 1 To arrCnt - bitCount
mask = "0" : mask
Next num
|
如果二进制数的位数小于子串的个数,则在二进制数
mask的前面补上"0"。比如,OriStr中有三个子串"light_01,light_02,light_03",BitValue的值为2,把十
进制的数"2"转化为二进制的数"10"。这时,二进制数的位数小于子串的个数,就需要在二进制数前面补上"0",使其变成"010"才能和三个子串一一
对应起来。
b). Loop…Repeat语句,其基本语法如下:
Loop
[statements]
[While | Until condition Do]
[statements]
Repeat
|
其中,Loop定义了循环的开始,Repeat定义了循环的结束。
While condition Do 的含义是,当条件为真的时候执行循环体中的代码。当条件为假的时候,退出循环。
Until condition Do 的含义正好相反,当条件为假的时候执行循环体中的代码。当条件为真的时候则退出循环。
pos = 1
start = 1
Ans=""
Loop
While pos <= arrCnt Do
idx = Index(proStr,separator,pos)
If mask[pos,1] = "1" Then
subStr = proStr[start,idx - start]
subStr=trim(subStr)
*make sure every substring matches the specified pattern
If subStr match "5A" : "_" : "2N" Then
Ans=Ans:",":subStr
End Else
Ans="error substring:" : subStr:".It does not match the pattern."
Call DSLogWarn(Ans,"TransBitMask")
GoTo ExitFunc
End
End
start = idx+1
pos+=1
Repeat
|
这段代码的功能是从输入的字符串中把每一个子串分离出来,然后判断每一个子串对应的二进制数中的值是否为"1"。如果其对应的数是"0",就直接进
行下一个循环去处理下一个子串。如果其对应的数是"1"则继续判断这个子串是否符合命名的规则,如果符合就把子串添加到输出字符串中;如果不符合规则就在
日志信息中提示用户输入的字符串有错误,之后跳出循环。
在循环语句中可以使用两个用来控制循环的关键字Continue和Exit:Continue用来结束本次循环,进行下一次循环;而Exit的功能是退出当前循环。
(5). 一些BASIC语言中处理字符串的函数。
- 使用冒号":"作为字符串连接的符号。
例如:"5A" : "_" : "2N" 等价于字符串 "5A_2N"
- 使用中括号[ ]来取出某个字符串的子串,其语法如下:
expression [ [ start, ] length ]
例如:strA="abcdef ", 那么strA[1,3]的意思就是从序号为1的字符开始,一共取出三个字符,其结果就是"abc"。BASIC语言中数组或者字符串的第一个元素的序号是1。
- Count (string, substring)用来计算字符串中的某个子串出现的次数。
例如:计算字符串"abacab"中的子串"ab"出现的次数,arrCnt = Count("abacab", "ab"),那么arrCnt就等于2。
- Len (string) 用来计算字符串中包含的字符的个数。
例如:Len("abc")的值就是3。
- TRIMF (string) 用来删掉字符串首部的空格。
TRIMB (string) 用来删掉字符串尾部的空格。
例如,字符串strA=" abc ",其首尾各有多个空格。经过strA= TRIMF (strA)和strA= TRIMB (strA)这样的处理就可以除掉字符串首尾的所有空格了,其结果是"abc"。
- Index (string, substring, instance) 用来得到字符串中的某个子串的位置。其中instance表示这个子串是在整个字符串中是第几次出现。
例如:字符串为"abacab",要计算子串"ab"第二次出现在字符串中的位置。Index ("abacab", "ab", 2 )的结果就是5。
(6). 读者看到代码中的这一行subStr match "5A" : "_" : "2N",也许会觉得迷惑,下面就重点讲解一下BASIC语言中的模式匹配规则(Pattern Matching)。
表1. DataStage BASIC语言中的模式匹配规则
模式名 |
含义 |
… |
任意类型任意个数的字符 |
0X |
任意类型任意个数的字符 |
nX |
n个任意类型的字符 |
0A |
任意个数的字母 |
nA |
n个字母 |
0N |
任意个数的数字 |
nN |
n个数字 |
‘string‘ |
单引号或者双引号中包含的具体字符串 |
~ |
不匹配某种模式 |
"5A" : "_" :
"2N"模式就是限制字符串只能包含八个字符,并且前五个字符必须是字母,第六个字符是下划线"_",最后两个字符必须是数字。字符串
"abcde_01"能匹配上这种模式,而字符串"abcde_012"或者"abcd_01"都不满足这种格式要求。
如果规则变成"5A" : "_" :
"~2N",这个模式就是限制字符串的前五个字符必须是字母,第六个字符是下划线"_",而最后两个字符必须是非数字的字符,例如:"abcde_mm"
能和这个规则匹配上。这时,字符串"abcde_01"不再符合新规则的要求,字符串"abcde_9a"也不能满足要求。
7.对编写好的代码进行编译。用户只需要点击图8中的编译(Compile)按钮即可,编译的结果出现在New Server
Routine编辑窗口中Code标签页面的下部区域内。使用编译器对程序进行编译不仅可以帮助用户检查程序中潜在的语法错误,更重要的是,编译成功后的
源代码将转化为可执行的对象代码(object code)。
8.单元测试编译后的代码。
用户点击图8中的测试(Test)按钮,DataStage弹出一个测试窗口。在窗口中填写输入变量的数据,然后点击"Run"按钮即可对这个函数
的功能进行测试,输出结果出现在"Result"字段里。在这个测试窗口中,用户还可以通过点击"Run
All"按钮同时执行多组测试数据(如图9所示)。
图9. DataStage中Routine的测试窗口
通过了单元测试,这个自定义函数就开发完成了。下一部分将为读者介绍在DataStage Server Job中如何使用这个函数对数据进行转化。
自定义函数的使用
下
面将用一个完整的实例来讲解如何在Transform
Stage中使用DataStage内置的和用户自定义的Routine来实现数据转化。这个实例将分别从DB2数据库和文本文件读取数据,再对抽取的数
据进行格式或者内容的转化后,将其插入到另一个用于存档的DB2数据库中。
创建Server Job的具体步骤如下:
步骤一:在一个名为ASSET的数据库中创建如下两个表SODS1.DUTYRESULT 和SODS2.ROOMS,并在表中插入数据:
DROP TABLE SODS1.DUTYRESULT
@
CREATE TABLE SODS1.DUTYRESULT
(
DUTYPERSON VARCHAR(30) NOT NULL,
DUTYTIME TIMESTAMP NOT NULL,
ROOM_NAME VARCHAR(35) NOT NULL,
LIGHTSTATUS INT NOT NULL,
ADD_DATE TIMESTAMP,
ADD_BY_USER_NAME CHARACTER(30)
)
@
|
这个表的数据记录了每次值班人员检查室内日光灯开关状态的结果。DUTYPERSON和DUTYTIME分别代表了值班人员的名称和值勤时间;
ROOM_NAME和LIGHTSTATUS代表被检查到的房间号和日光灯照明的状况;ADD_DATE和ADD_BY_USER_NAME则是在表中添
加该记录的人员名称和添加时间。
DROP TABLE SODS2.ROOMS
@
CREATE TABLE SODS2.ROOMS
(
ROOM_NAME VARCHAR(35) NOT NULL,
LIGHTLIST VARCHAR(255) NOT NULL,
ADD_DATE TIMESTAMP,
ADD_BY_USER_NAME CHARACTER(12)
)
@
ALTER TABLE SODS2.ROOMS
ADD CONSTRAINT ROOMS_PK
PRIMARY KEY (ROOM_NAME)
@
|
这个表中记录了每个房间内安装的所有日光灯的名称列表。ROOM_NAME是房间的名称,也是这个表的主键。LIGHTLIST是该房间内安装的日光灯的名称列表。ADD_DATE和ADD_BY_USER_NAME则是在表中添加该记录的人员名称和添加时间。
在一个名为ASSTARCH的数据库中创建表SODS2.DUTYRESULT:
DROP TABLE SODS2.DUTYRESULT
@
CREATE TABLE SODS2.DUTYRESULT
(
REC_ID BIGINT GENERATED ALWAYS
AS IDENTITY( START WITH 1,INCREMENT BY 1,CACHE 20 ),
DUTYPERSON VARCHAR(30) NOT NULL,
DUTYTIME TIMESTAMP NOT NULL,
ROOM_NAME VARCHAR(35) NOT NULL,
ONLIGHT VARCHAR(255) NOT NULL,
ADD_DATE TIMESTAMP,
ADD_BY_USER_NAME CHARACTER(30)
)
@
ALTER TABLE SODS2.DUTYRESULT
ADD CONSTRAINT DUTYRESULT_PK
PRIMARY KEY (REC_ID)
@
|
这个表中的数据是归档信息,记录了值班人员检查到的每个房间开着的日光灯列表。REC_ID是表中记录的标号,是由数据库自动生成的。这个编号由1
开始,每条记录的编号增加1,因此每个编号都是唯一的,REC_ID就是这个表的主键。DUTYPERSON和DUTYTIME分别代表了值班人员的名称
和值勤时间;ROOM_NAME和ONLIGHT代表被检查到的房间号和打开状态的日光灯列表;ADD_DATE和ADD_BY_USER_NAME则是
在表中添加该记录的人员名称和添加时间。
步骤二:准备好所需的数据之后,打开DataStage Designer,创建一个名为assetArch的Server
Job。之后,在Repository View中选择Table Definitions,点击右键后出现的菜单中选择Import‘Plug-in
Meta Data
Definitions,再在弹出的数据库类型列表窗口中选择DSDB2,这样就可以引入DB2类型数据库中的表的定义了。用这样的方法分别引入
ASSET数据库中表SODS1.DUTYRESULT的定义以及ASSTARCH数据库中表SODS2.DUTYRESULT的定义。
DataStage中表的定义(Table
Definitions)详细说明了每一个Stage中用到的数据的类型,而Stage中用到的数据不一定仅仅来源于数据库中的一个表,因此每一个
Stage中装载进来的(load)的表的定义也并不一定等于数据库中某个表的定义。本例中抽取的数据来源于两个表,因此并不是直接使用引入的表
SODS1.DUTYRESULT的定义,而是要对其进行一些修改。在Table
Definition目录下找到这个表的定义,双击它即可弹出编辑窗口。在Columns标签页中添加一行字段的定义,字段名为"LIGHTLIST",
类型为Varchar(255),不可为空值,如图10所示。后面读者马上将会看到新添字段的值来源于ASSET数据库中的另一个表
SODS2.ROOMS。再有,因为表SODS2.DUTYRESULT的第一个字段REC_ID是数据库自动生成的,不需要为这个字段指定插入的数据,
所以要从装载进来的表定义中删掉这一列。
图10. 表定义的编辑窗口
步骤三:在Server Job的编辑窗口内,添加两个DB2
Stage分别用来从DB2数据库抽取数据,以及把处理过的数据保存到DB2数据库中;两个Transformer
Stage用来对抽取的数据进行必要的处理;还有一个Sequential File
Stage用来从一个纯文本的文件中读取数据。然后,按照数据流动的方向把它们用连线连接起来,如图11。
图11. 本文创建的Server Job的结构
步骤四:编辑名为Asset的DB2
Stage,使它连接到名为ASSET的数据库上,并且在这个Stage的Output的标签页中装载进来刚才修改过的表
SODS1.DUTYRESULT的定义,在SQL子标签页中自定义查询数据的语句(如图12)。从自定义的SQL查询语句可以看出这个Stage中的数
据来源于ASSET数据库的两个表,刚才在表SODS1.DUTYRESULT的定义中添加的字段"LIGHTLIST"来源于表
SODS2.ROOMS,这也是刚才修改表定义的原因。
图12. 在DB2 Stage中自定义查询数据的语句
步骤五:在编辑名为Asset_Data_File的Sequential File Stage之前,要先在Table
Definitions中引入这个Stage读取的数据文件assetData.csv的结构。assetData.csv是一个纯文本文件,其中的数据
如图13所示,所以引入的表的定义中每个字段都是varchar类型的。
图13. assetData.csv文件中的数据
现在可以编辑名为Asset_Data_File的Sequential File Stage了,在Outputs标签页中指定assetData.csv所在路径,并且装载进来这个文本文件的表定义。
步骤六:编辑名为AssetArch的DB2 Stage,使它连接到名为ASSTARCH的数据库上。然后为输入这个Stage的两条路径指定目标表的名称SODS2.DUTYRESULT,并装载进来这个表的定义。
步骤七:编辑名为Transformer_1的Transformer Stage。大多数输入和输出的字段可一一对应起来,就象图14所示。
图14. 编辑中的Transformer Stage,大多数输入和输出的字段可一一对应起来
从上图可以看到输出的字段ONLIGHT不能直接和左边输入的字段对应起来,而且此处就需要应用前面自定义的Routine对输入的数据进行转化。
首先双击字段ONLIGHT的Derivation区域对其进行编辑,再点击右键,在弹出的菜单中选择DS
Routine。这时,DataStage中保存的所有routine都被展示出来。从中选取新创建的routine TransBitMask(
),再分别把输入的字段DSLink4.LIGHTLIST和DSLink4.LIGHTSTATUS作为这个函数的第一个和第三个输入变量。此处把
TransBitMask(
)的第二个输入变量设置为空串即可,因为当这个变量为空串时,函数就会使用缺省值分号作为分隔符。所有的输入字段和输出字段就这样被关联起来了,如图15
所示。
图15. Transformer Stage中输入和输出的字段被关连起来
步骤八:编辑名为Transformer_10的Transformer
Stage,它用来对文本文件中的数据进行转化,保证这些数据能被正确的插入目标表中。如图13所示,文本文件每一条数据中的两个日期都是格式为
DATE.TAG(具体来说就是YYYY-MM-DD格式)的字符串,因此需要把这两个输入字段的Data
Element的值设定为DATE.TAG。然后编辑对应的输出字段的Derivation,先使用DataStage内置的TAG.TO.DATE()
函数把输入值的类型从DATE.TAG格式的字符串转化为DataStage内部的数字格式,再使用Timestamp()函数把数字格式的日期值转化为
DB2可以识别的Timestamp类型,如图16所示。
图16. 可以在Transformer Stage中对输入的数据的格式进行转化
经过上面的八个步骤就完成了整个Server Job的编辑工作,对它进行编译后就可以运行这个Server Job了。
运行并验证Server Job
运行之前,连接到名为asset的数据库,检查一下数据库中现有的数据状况。
select a.dutyperson,
a.dutytime,
a.room_name,
b.lightlist,
a.lightstatus,
a.add_date,
a.add_by_user_name
from sods1.dutyresult a , sods2.rooms b
where a.room_name = b.room_name
|
表2. SQL查询语句的执行结果
DUTY
PERSON |
DUTYTIME |
ROOM_NAME |
LIGHTLIST |
LIGHT
STATUS |
ADD_DATE |
ADD_BY_USER_NAME |
Joyce |
2006-1-1 9:00:00.000000 |
classroom3951 |
light_01, light_02, light_03, light_04, light_05 |
28 |
2006-1-1 15:00:00.000000 |
Joyce |
David |
2006-1-2 18:00:00.000000 |
classroom3300 |
light_01, light_02, light_03, light_04, light_05, light_06, light_07, light_08 |
96 |
2006-1-2 20:00:00.000000 |
Joyce |
Edison |
2006-3-5 18:00:00.000000 |
classroom2417 |
light_01, light_02, light_03, light_04 |
12 |
2006-3-20 20:00:00.000000 |
David |
Joyce |
2006-2-1 19:00:00.000000 |
classroom3951 |
light_01, light_02, light_03, light_04, light_05 |
1 |
2006-6-1 15:00:00.000000 |
Joyce |
再连接到ASSTARCH数据库,
select * from SODS2.DUTYRESULT
这个表目前是空的,没有任何数据。
在DataStage Designer中执行Server
Job,当看到连接Stage的连线都变成绿色,就说明它执行成功了,如图17所示。而且从连线上的信息,可以清楚地看到从DB2数据库中抽取出4条数据
经过处理后被插入到目标中,而从文本文件中抽取了3条数据经过处理后也被插入到目标中。
图17. 运行成功的Server Job
这时,再次连到ASSTARCH数据库,
select * from SODS2.DUTYRESULT
表3. Server Job执行成功后,表SODS2.DUTYRESULT中的数据
REC_ID |
DUTYPERSON |
DUTYTIME |
ROOM_NAME |
ONLIGHT |
ADD_DATE |
ADD_BY_USER_NAME |
1 |
Joyce |
2006-1-1 9:00:00.000000 |
classroom3951 |
light_01,light_02,light_03 |
2006-1-1 15:00:00.000000 |
Joyce |
2 |
David |
2006-1-2 18:00:00.000000 |
classroom3300 |
light_02,light_03 |
2006-1-2 20:00:00.000000 |
Joyce |
3 |
Edison |
2006-3-5 18:00:00.000000 |
classroom2417 |
light_01,light_02 |
2006-3-20 20:00:00.000000 |
David |
4 |
Joyce |
2006-2-1 19:00:00.000000 |
classroom3951 |
light_05 |
2006-6-1 15:00:00.000000 |
Joyce |
5 |
Joyce |
2005-1-2 0:00:00.000000 |
classroom3951 |
light_02,light_03,light_05 |
2005-1-2 0:00:00.000000 |
Joyce |
6 |
Aaron |
2005-1-3 0:00:00.000000 |
classroom3742 |
light_05 |
2005-1-5 0:00:00.000000 |
Joyce |
7 |
David |
2005-1-10 0:00:00.000000 |
classroom3300 |
light_04,light_05 |
2005-1-10 0:00:00.000000 |
Joyce |
现在分别追踪一下两个数据源中的数据,来验证一下之前对两个Transform
Stage的编辑是否产生效果。首先对本文文件中的数据进行检测,图13中第两条数据的日期分别是"2005-1-3"和"2005-1-5",这条数据
对应于表3中的第六条数据,可见这两个日期值已经被转化为"2005-1-3 0:00:00.000000"和"2005-1-5
0:00:00.000000"。再来检验一下从DB2数据库中抽取的数据,对于表2中的第一条数据,"light_01,light_02,
light_03,light_04,light_05"和28是自定义函数TransBitMask()的输入变量。十进制数28转化为二进制数
"11100",其每一位和"light_01,light_02,light_03,light_04,light_05"中的五个子串一一对应起来,
只有前三个子串对应的数值是"1",所以它们添加到输出字符串中,因此TransBitMask的输出结果应该是"light_01,light_02,
light_03"。这和表3中的第一条数据是一致的。
通过上面的验证,读者可以清楚地看到在Transform Stage中内置的以及自定义的函数成功地对数据的格式和内容进行了转化。
自定义Routine的可重用性
使用DataStage Administrator创建一个项目叫做new,然后打开DataStage
Manager,可以从资料列表中清楚地看到在本机上共有两个项目(Project),一个叫做test,另一个则是新建的项目new。在本文第三部分中
创建的名为TransBitMask的自定义routine是在项目test中的,而新建的项目new的Routines列表中只有DataStage内
置的功能函数,如图18所示。这说明用户自定义的Routine只能被其所在的项目使用,并不会出现在其它项目的Routines列表中,也就不能被其它
项目直接使用了。
图18. DataStage Manager中的资料列表
那如何才能让在一个项目中用户开发的Routine可以被其它项目使用呢?其实答案很简单。用户只需要使用DataStage
Manager的Export功能,首先把那些希望被重用的routine、server
job甚至是整个项目导出,然后再使用Import功能根据需要把它们导入到新的项目中具体做法如下:
在DataStage Manager先选择项目test,从菜单中选择Export ‘DataStage Component,就会弹出Export窗口,如图19所示。
图19. DataStage Manager中的Export窗口
在这个窗口中可以选择是导出整个项目还是该项目中的某个部分,所有导出的内容被保存在名字为*.
dsx的文本文件中。用户还可以选中Export as XML
document选项,这样就可以把导出的内容保存在一个XML格式的文件中,相应地这个文件的名称就是*.
xml。本文只需要把自定义的routine导出成一般文本文件,所以仅选中Routines选项,并在下拉列表中选择其所在的目录。然后点击
Export按钮,即可在指定的目录下生成名为DSExport. dsx文件。
下一步就是把导出的*. dsx(或者*. xml文件)导入到新的项目中去了。先选择新建的项目new,然后在DataStage Manager的菜单中选择Import ‘DataStage Component,就会弹出Import窗口,如图20所示。
图20. DataStage Manager中的Import窗口
在指定需要引入的文件路径后点击OK按钮,这样test项目中导出的功能函数TransBitMask就可以顺利导入到项目new中了,结果如图21所示。也就是说,在项目new中也可以使用自定义函数TransBitMask对数据进行转化了。
图20. DataStage Manager中的资料列表
总结
本文前两部分概述了DataStage的特点以及Transformer Stage的使用方法。第三部分重点介绍如何使用DataStage
BASIC语言开发一个用户自定义的功能函数,并且讲解了DataStage BASIC语言的基本语法。第四部分以一个完整的Server
Job实例详细地为读者展示了在Transformer
Stage中如何使用系统内置的和自定义的函数来转化数据。文章的最后介绍了如何重用自定义的功能函数。通过阅读本文,读者对DataStage
Basic语言有了初步的了解。读者可以参照这个实例和后面的参考资料,逐步学会如何开发满足实际需要的功能函数。
|