pb(powerbuilder)数据管道使用解析

工作需要制作两个sqlserver数据库同步的工具,但源数据库和目标数据库同步的表的表结构不同,就是字段不是对应的,而且字段数量也不一致。参考了网上和书上的很多资料,但都没能很好的解决这个问题,主要是这些资料讲的太过表面,很多细致的问题没有涉及,而实际应用又讲的很复杂更看不懂,好多步骤都略过了。程序实例也看过一些,有了一定了解。像笔者这样仅有两三年学习经历的学者来说,这些远远不够。后来经过反复实践,最终还是搞懂了很多零碎的问题,使之解决。下面复述这些问题。

在这之前你需要了解数据管道的基础知识,就是看看网上的pb程序实际例子,即安装pb12.5,下载别人的pb程序自己打开看,下载不了就充会员,源代码不付费基本很难下载到。再就是看书,看看pb10的书也行,没有自己到淘宝搜,高级的别看。然后你才能看懂下面的。

笔者使用的数据库是sqlserver2008R2,源数据库的源表是通过sqlselect方式建立的,是三个表的集合,只取了三个表中的部分字段,然后同步到目标数据库的目标表,目标表前部分的字段和源表是一一对应的,但最后面有三个字段源表中是没有的。

*1是源数据库,2是目标数据库。

这样的两个表同步,用数据管道画板直接建立的管道在这个例子中是不能使用的。虽然执行管道start会有返回值,但很多情况都是返回-1,readrows,writerows,errorrows都是0,看起来就是没反应。数据管道中也可以直接定义具体的源表数据筛选规则,例如列名=?,单号=?,但这样的筛选必须使用具体值,不能用变量,很不方便。

*建立完数据管道如果再修改的话,会生成一个query,打开点最下面的where,可以根据列的具体值进行筛选。where旁边还有其他的功能。

数据管道和生成的query文件(暂且这么说吧),都可以点右键选择source code打开,就能看到源代码。数据管道也可以直接双击打开,然后点工具栏的datasource按钮,图标是个蓝色书本的那个,也可以打开数据管道源代码。

*蓝色书本右侧的蓝色书本带个X的,代表execute,就是执行,你点他就会执行数据管道复制数据。

如果你是初次使用,或者不太用数据管道,建议一定要用数据管道画板建立数据管道,而不是直接用函数创建数据管道语法,因为后者基本是百分之百会出错,你再调试搭进去时间得不偿失。用画板建立管道后可以根据实际需要一步步的再改。

上图是数据管道画板,大红框里面,table指源数据库的源表,key写什么都不影响,它不是指源表或者目标表的主键的那个key。 Option是选择操作方式,create创建表/记录,replace替换表/记录,refresh先删除然后插入表或记录,append追加表或记录,update更新表或记录。你能看懂英语这些就不是问题。Maxerror是指一次运行管道errorrow错误的最大值,超过这个值管道停止,不论还有没有未同步的数据。Commit指多少条记录提交一次事务,100就是复制100条数据后提交生效。事务是什么意思?事务指数据库在insert,update,delete后,数据库都不会立即修改数据,只有提交了commit这个操作---事务 后,数据库才会修改成最新的值。设置多少根据实际需要来。Extended attributes字面翻译是扩展属性,没用过。

你看下面的表有源表字段和目标表字段,需要指出的是,源表字段名称和目标表字段名称可以不一致(上面图里面是一样的),但是源表字段的数据类型与目标表对应的字段的数据类型必须一致,源表字段是string目标表对应字段必须是string,否则管道start失败,返回结果-1,-1就是管道打开失败,一般是管道语法有错误。目标表字段一定要大于源表对应字段的宽度(Width),否则写入目标表字段宽度不够导致溢出也是管道打开失败,返回值-1。笔者的目标表还有个identity的自增字段,一定要把identity字段初始值设定为exclude,意思就是这个字段不同步,见上面图右下的那个小红框。否则start管道返回值-1,没提示。这里顺便提一下,如果你的sqlserver源表字段类型是numeric(18,4),小数点后有4位,那么接收这个值的pb变量数据类型必须用decimal而且有效位数、小数点位数大于等于numeric的有效位数、小数点位数,也就是至少是decimal(18,4)。如果pb变量数据类型用interger或者long结果一定出错,而且语法不提示错误。

数据管道创建好后再创建数据管道用户实例,然后使用“数据管道用户实例.start” 运行管道,运行管道前有很多步骤,比如定义创建事务(transaction),连接源数据库目标数据库,定义管道对象,这些请自行参考其他资料。运行数据管道可以获取到返回值,其中有至少18种返回值,比如-1表示“管道打开错误”,这个错误有很多可能性,其中以数据管道语法中的retrieve语法错误居多。-16表示“源数据库错误”,源数据库错误不是指源数据库连接错误,而是指源数据库读取错误,比如某列的数据类型不符或者其他原因读取失败。

如果源数据库的源表和目标数据库的目标表字段数量相同且字段顺序一一对应,那么使用数据管道画板建立的数据管道源代码语句类似下面这样(为了不占用过多的行,笔者删去了一部分行便于观看,删除的行不影响功能,下同):

pipe_hryy_stobuy sourcecode

PIPELINE(source_connect=1,destination_connect=2,type=create,commit=100,errors=100,keyname="StoreinOrder_x")

SOURCE(name="StoreinOrder/StoreinOrder_DETAIL/COMMODITY",COLUMN(type=varchar,name="StoreinOrder.billno",dbtype="varchar(20)",key=yes,nulls_allowed=no)

 COLUMN(type=long,name="StoreinOrder_DETAIL.myrow_id",dbtype="int",nulls_allowed=no)

 COLUMN(type=varchar,name="StoreinOrder.SUP_Name",dbtype="varchar(128)",nulls_allowed=yes)

 COLUMN(type=varchar,name="COMMODITY.COM_NAME",dbtype="nvarchar(128)",nulls_allowed=no)

 COLUMN(type=varchar,name="COMMODITY.SPECIFICATION",dbtype="nvarchar(256)",nulls_allowed=yes))

RETRIEVE(statement="PBSELECT( VERSION(400) TABLE(NAME=~"StoreinOrder~" )  TABLE(NAME=~"StoreinOrder_DETAIL~" )  TABLE(NAME=~"COMMODITY~" ) COLUMN(NAME=~"StoreinOrder.billno~") COLUMN(NAME=~"StoreinOrder_DETAIL.myrow_id~") COLUMN(NAME=~"StoreinOrder.SUP_Name~") COLUMN(NAME=~"COMMODITY.COM_NAME~") COLUMN(NAME=~"COMMODITY.SPECIFICATION~") COLUMN(NAME=~"COMMODITY.UNIT~") COLUMN(NAME=~"COMMODITY.ORIGIN~") COLUMN(NAME=~"StoreinOrder_DETAIL.BATCH_NO~") COLUMN(NAME=~"StoreinOrder_DETAIL.validity~") COLUMN(NAME=~"StoreinOrder_DETAIL.QUANTITY~") COLUMN(NAME=~"StoreinOrder.doc_person~") COLUMN(NAME=~"StoreinOrder.THISDATE~")    JOIN (LEFT=~"StoreinOrder_DETAIL.billno~"    OP =~"=~"RIGHT=~"StoreinOrder.billno~" )    JOIN (LEFT=~"StoreinOrder_DETAIL.COM_ID~"    OP =~"=~"RIGHT=~"COMMODITY.COM_ID~" ) )")

DESTINATION(name="StoreinOrder",COLUMN(type=varchar,name="billno",dbtype="varchar(20)",key=yes,nulls_allowed=no,initial_value="spaces")

 COLUMN(type=long,name="myrow_id",dbtype="int",nulls_allowed=no,initial_value="0")

 COLUMN(type=varchar,name="SUP_Name",dbtype="varchar(128)",nulls_allowed=yes)

 COLUMN(type=varchar,name="COM_NAME",dbtype="nvarchar(128)",nulls_allowed=no,initial_value="spaces")

 COLUMN(type=varchar,name="SPECIFICATION",dbtype="nvarchar(256)",nulls_allowed=yes))

 因为word的排版格式,有的行看起来格式有点乱。Pipe开头部分,source部分,destination部分不说了,主要看RETRIEVE部分。注意看表名或者列明引用的双引号”前面都有一个波浪线~,这个波浪线的作用是pb的转义,表示波浪线后面的双引号是个字符串内部的双引号,而不是代表字符串的开始或者结束。就是加了波浪线代表这里不是字符串断开的地方。其实如果需要同步具体的指定的行,比如指定某个单号行号,可以在retrieve尾部直接添加语法。如果配合函数还可以直接添加变量,用变量控制数据,实现动态数据管道。添加的位置就在retrieve结尾和destination开始中间,那个红色字体和蓝色字体之间的位置。比如可以加上下面的语句:

WHERE(    EXP1 =~"StoreinOrder.billno~"   OP =~"=~"    EXP2 =~"'SLJO23090700135'~" )

它代表只同步StoreinOrder表billno单号字段值为SLJO23090700135的行,可能是一行或者多行。添加后如下,绿色是添加部分:

pipe_hryy_stobuy sourcecode

PIPELINE(source_connect=1,destination_connect=2,type=create,commit=100,errors=100,keyname="StoreinOrder_x")

SOURCE(name="StoreinOrder/StoreinOrder_DETAIL/COMMODITY",COLUMN(type=varchar,name="StoreinOrder.billno",dbtype="varchar(20)",key=yes,nulls_allowed=no)

 COLUMN(type=long,name="StoreinOrder_DETAIL.myrow_id",dbtype="int",nulls_allowed=no)

 COLUMN(type=varchar,name="StoreinOrder.SUP_Name",dbtype="varchar(128)",nulls_allowed=yes)

 COLUMN(type=varchar,name="COMMODITY.COM_NAME",dbtype="nvarchar(128)",nulls_allowed=no)

 COLUMN(type=varchar,name="COMMODITY.SPECIFICATION",dbtype="nvarchar(256)",nulls_allowed=yes))

RETRIEVE(statement="PBSELECT( VERSION(400) TABLE(NAME=~"StoreinOrder~" )  TABLE(NAME=~"StoreinOrder_DETAIL~" )  TABLE(NAME=~"COMMODITY~" ) COLUMN(NAME=~"StoreinOrder.billno~") COLUMN(NAME=~"StoreinOrder_DETAIL.myrow_id~") COLUMN(NAME=~"StoreinOrder.SUP_Name~") COLUMN(NAME=~"COMMODITY.COM_NAME~") COLUMN(NAME=~"COMMODITY.SPECIFICATION~") COLUMN(NAME=~"COMMODITY.UNIT~") COLUMN(NAME=~"COMMODITY.ORIGIN~") COLUMN(NAME=~"StoreinOrder_DETAIL.BATCH_NO~") COLUMN(NAME=~"StoreinOrder_DETAIL.validity~") COLUMN(NAME=~"StoreinOrder_DETAIL.QUANTITY~") COLUMN(NAME=~"StoreinOrder.doc_person~") COLUMN(NAME=~"StoreinOrder.THISDATE~")    JOIN (LEFT=~"StoreinOrder_DETAIL.billno~"    OP =~"=~"RIGHT=~"StoreinOrder.billno~" )    JOIN (LEFT=~"StoreinOrder_DETAIL.COM_ID~"    OP =~"=~"RIGHT=~"COMMODITY.COM_ID~" )WHERE(    EXP1 =~"StoreinOrder.billno~"   OP =~"=~"    EXP2 =~"'SLJO23090700135'~" ) )")

DESTINATION(name="StoreinOrder",COLUMN(type=varchar,name="billno",dbtype="varchar(20)",key=yes,nulls_allowed=no,initial_value="spaces")

 COLUMN(type=long,name="myrow_id",dbtype="int",nulls_allowed=no,initial_value="0")

 COLUMN(type=varchar,name="SUP_Name",dbtype="varchar(128)",nulls_allowed=yes)

 COLUMN(type=varchar,name="COM_NAME",dbtype="nvarchar(128)",nulls_allowed=no,initial_value="spaces")

 COLUMN(type=varchar,name="SPECIFICATION",dbtype="nvarchar(256)",nulls_allowed=yes))

上面这个数据管道代码是可以运行成功的,笔者亲自测试过,只不过实际测试的源代码比上面这个还要复杂。

如果将具体单号换成变量,比如pb中定义变量bill, 数据类型string,上面的语法就需要做大改变,因为变量的引入需要断开字符串重新连接字符串,双引号必须要转义,retrieve语句可以改成:

RETRIEVE(statement="PBSELECT( VERSION(400) TABLE(NAME=~"StoreinOrder~" )  TABLE(NAME=~"StoreinOrder_DETAIL~" )  TABLE(NAME=~"COMMODITY~" ) COLUMN(NAME=~"StoreinOrder.billno~") COLUMN(NAME=~"StoreinOrder_DETAIL.myrow_id~") COLUMN(NAME=~"StoreinOrder.SUP_Name~") COLUMN(NAME=~"COMMODITY.COM_NAME~") COLUMN(NAME=~"COMMODITY.SPECIFICATION~") COLUMN(NAME=~"COMMODITY.UNIT~") COLUMN(NAME=~"COMMODITY.ORIGIN~") COLUMN(NAME=~"StoreinOrder_DETAIL.BATCH_NO~") COLUMN(NAME=~"StoreinOrder_DETAIL.validity~") COLUMN(NAME=~"StoreinOrder_DETAIL.QUANTITY~") COLUMN(NAME=~"StoreinOrder.doc_person~") COLUMN(NAME=~"StoreinOrder.THISDATE~")    JOIN (LEFT=~"StoreinOrder_DETAIL.billno~"    OP =~"=~"RIGHT=~"StoreinOrder.billno~" )    JOIN (LEFT=~"StoreinOrder_DETAIL.COM_ID~"    OP =~"=~"RIGHT=~"COMMODITY.COM_ID~" ) WHERE(    EXP1 =~"StoreinOrder.billno~"   OP =~"=~"    EXP2 =~"'" + bill + "'~" ))")

绿色是加入的部分,注意bill的开始和结束有两个单引号,这是因为在sqlserver中where后面的单号 billno='' 是有单引号的。而且字符串开始结束需要用双引号,看不懂多看两遍。

实际上像这样改了变量的数据管道语法是不能运行的,运行管道start返回值-1,就是数据管道打开失败,就是retrieve的语法出了错误。是不是因为加入的部分破坏了RETRIEVE部分的完整,不得而知。是不是语法上不支持也不确定。但加入上面绿色部分的retrieve代码,数据管道确实是无法运行的,谁能看出是哪里出了问题?或者引入变量怎么改?看下面。

Pb功能强大,除了直接使用画板编辑,还可以直接编辑source code,也就是前面提到的那个蓝色的小书本图标。双击数据管道,打开后点菜单栏最底行的蓝色小书本,进入代码编辑状态。

  Source code源代码里面直接就是sqlserver的语句,是可以在sqlserver management studio 里面直接查询执行的。你需要把这个sqlserver语句做个小修改,比如添加一列(为什么要添加一列,因为只有改变了代码,你才能看到以下的变化。或者你有其他办法能改变代码也是可以的),如下:

SELECT StoreinOrder.billno,   

         StoreinOrder_DETAIL.myrow_id,   

         StoreinOrder.SUP_Name,   

         COMMODITY.COM_NAME,   

         COMMODITY.SPECIFICATION,   

getdate() UPD_DATE,

0 ROW_ID  

    FROM StoreinOrder,   

         StoreinOrder_DETAIL,   

         COMMODITY  

   WHERE ( StoreinOrder_DETAIL.billno = StoreinOrder.billno ) and  

         ( StoreinOrder_DETAIL.COM_ID = COMMODITY.COM_ID )

蓝色的两行就是加的,也可以只加一行。完成后点save保存,并关闭数据管道画板窗口。然后在pb左侧 数据管道名称上点右键,选择Edit Source,点开看看数据管道语法的代码有没有什么变化。

PIPELINE(source_connect=1,destination_connect=2,type=append,commit=1,errors=100,keyname="ERPBUYPLAN")

SOURCE(name="StoreinOrder/StoreinOrder_DETAIL/COMMODITY",COLUMN(type=varchar,name="StoreinOrder.billno",dbtype="varchar(20)",key=yes,nulls_allowed=no)

 COLUMN(type=long,name="StoreinOrder_DETAIL.myrow_id",dbtype="int",nulls_allowed=no)

 COLUMN(type=varchar,name="StoreinOrder.SUP_Name",dbtype="varchar(128)",nulls_allowed=yes)

 COLUMN(type=varchar,name="COMMODITY.COM_NAME",dbtype="nvarchar(128)",nulls_allowed=no)

 COLUMN(type=varchar,name="COMMODITY.SPECIFICATION",dbtype="nvarchar(256)",nulls_allowed=yes))

RETRIEVE(statement="SELECT StoreinOrder.billno,   

         StoreinOrder_DETAIL.myrow_id,   

         StoreinOrder.SUP_Name,   

         COMMODITY.COM_NAME,   

         COMMODITY.SPECIFICATION,   

getdate() UPD_DATE,

0 ROW_ID  

    FROM StoreinOrder,   

         StoreinOrder_DETAIL,   

         COMMODITY  

   WHERE ( StoreinOrder_DETAIL.billno = StoreinOrder.billno ) and  

         ( StoreinOrder_DETAIL.COM_ID = COMMODITY.COM_ID )")

DESTINATION(name="dbo.ERPBUYPLAN",COLUMN(type=varchar,name="SRC_PROOF_ID",dbtype="varchar(20)",key=yes,nulls_allowed=no,initial_value="spaces")

 COLUMN(type=long,name="SRC_LINE_ID",dbtype="int",key=yes,nulls_allowed=no,initial_value="0")

COLUMN(type=varchar,name="SUPPLIER_NAME",dbtype="nvarchar(128)",nulls_allowed=no,initial_value="spaces")

COLUMN(type=varchar,name="COM_NAME",dbtype="nvarchar(128)",nulls_allowed=no,initial_value="spaces")

 COLUMN(type=varchar,name="SPECIFICATION",dbtype="nvarchar(256)",nulls_allowed=yes))

 Pipeline开头部分,source部分,destination部分和之前一样没有改变,但是retrieve部分变了,变成你修改的sqlserver语句了,而且一模一样。Retrieve中双引号中的部分直接放到sql查询里面可以直接运行。

 到了这个步骤,你就可以做动态管道加入自己的变量了,而且可以使用函数创建动态数据管道,只需要在上面代码的蓝色和红色之间加入sql语句就可以了。下面是笔者的构建动态数据管道的函数:先通过数据管道用户实例的syntax属性获取到语法并赋给字符串变量ll_s_pos,调用下面的函数就可以加入单号变量billno,注意billno两侧必须加单引号。带变量的动态sql语句也需要仔细研究语法表达,因为即使是有一个符号不对,例如少了一个单引号或者括号,pb不会提示错误,但sql执行时就会出错。

String ls_start , ls_end , ls_where

long ll_s_pos

ll_s_pos = pos(a_sql,"DESTINATION")

ls_start = mid(a_sql,1,ll_s_pos - 6)

ls_end =  mid(a_sql,ll_s_pos - 5)

ls_where=" and storeinorder.billno = '" + billno + "'"

return ls_start+ls_where+ls_end

这个函数比较简单,你也可以不通过syntax属性获取语法,而是直接用函数构造数据管道语法。如前面所说,如果你对数据管道语法很熟练了,这样做应该没什么问题。

至此笔者终于明白,如果源数据库源表和目标数据库目标表完全一致,即字段数量,字段类型完全一致且一一对应,那使用数据管道画板创建的管道就可以成功运行,无需编辑源代码;如果源数据库源表和目标数据库目标表不一致,如字段数量不同,则必须通过编辑源代码,改变数据管道的语法才能成功运行管道。而且这两种方式产生的数据管道语法的retrieve部分完全不同。且只有后一种方式即通过编辑源代码,才能加入变量构造动态数据管道成功。笔者运行的pb版本是version 12.52 build5629 。

还遇到了一些问题:

加入变量使用动态数据管道后,运行数据管道,管道执行结果返回代码1,readrows,writerows有计数,保存错误的数据窗口没有任何数据,但是目标数据库的目标表中没有数据写入。查找原因可以在目标数据库服务器上使用sql追踪查看powerbuilder是否进行了insert操作。具体方法是打开SQL Server Profiler,新建跟踪,确定后打开“跟踪属性”窗口,点“事件选择”选项卡,右下的“显示所有列”打勾,点“列筛选器”,在application name右侧的类似于下面输入“powerbuilder”,如下图:

确定后运行,再运行数据管道。如果目标数据库中事件追踪中有powerbuilder程序的insert语句,但就是写入数据库不成功,首先检查sqlserver问题,包括检查数据库和表的权限。再看pb中源数据库、目标数据库连接的语句是否都正确,transaction定义中每个行是否都正确。需要注意的是即使错一个字符也是不行的,某些低级错误在pb保存时没有语法错误提示,执行也不报错,不仔细看很难发现。

 数据管道用户实例通过start运行时需要指定一个数据窗口(例如Dw_error),用于返回在目标数据库目标表上errorrows的错误结果。数据窗口Dw_error中第一列就是报错返回目标数据库目标表操作错误原因,第二列开始是insert或者update/delete的语句的数据,这提供了目标数据库上的出错结果的显示。

数据管道执行返回结果为“管道打开错误”,返回值为-1,是因为管道语法中retrieve语句写的不对,有可能是结尾的英文双引号或者右括号忘记加。运行管道后readrows,writerows,errorrows全是0,是管道语法错误,即retrieve的sql语句错误,检查sql语句。

数据管道执行完毕后还可以进行写入验证,方法很多,比如从目标数据库读取出写入的单号,即证明写入成功。

以上便是笔者研究数据管道的经验,耗费了近两个月的时间,而且每遇到不能解决的问题时笔者就近乎失眠。后来终于渐渐恢复了。希望上述经验能够帮助到需要学习pb数据管道的人。

2023年10月11日

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PB PowerBuilder 是一种用于开发各种商业应用程序的集成开发环境(IDE),它提供了许多强大的功能和工具。如果要在 PB PowerBuilder 中实现类似 Excel 的筛选功能,需要使用一些特定的技术和方法。 首先,我们可以使用 DataWindow 控件来实现数据的筛选。通过设置 DataWindow 的过滤条件和排序功能,可以轻松实现数据的筛选。 其次,可以使用 DataStore 对象来处理数据的检索和筛选。DataStore 是一个用于缓存和处理数据的对象,它可以使用 SQL 查询语句来检索和筛选数据。 另外,可以通过使用 DataWindow 的自定义过滤功能来实现更精确的筛选。自定义过滤允许用户根据特定的条件和选择来筛选数据,例如使用特定列的值进行筛选。 此外,通过使用 PowerScript 编写自定义代码,可以实现更复杂的筛选逻辑。PowerScript 是 PB PowerBuilder 的编程语言,可以使用它来编写事件和函数,以实现特定的功能和逻辑。 最后,可以使用 PB PowerBuilder 的表格控件来显示筛选后的数据。表格控件允许用户以表格的形式查看和编辑数据,可以使用 DataWindow 控件和 DataStore 对象中的数据来填充表格。 总之,要在 PB PowerBuilder 中实现类似 Excel 的筛选功能,可以使用 DataWindow 控件、DataStore 对象和 PowerScript 编写自定义代码来实现数据的检索、筛选和显示。这些功能的结合使用可以为用户提供类似于 Excel 的筛选体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值