问题解决 SLT写入数据库NULL值
一
前言
最近项目上使用SLT(SAP Landscape Transformation,是一个实时数据复制工具,主要用于将数据从源系统(如SAP ERP或非SAP系统)复制到目标系统)把SAP数据同步到ORACLE数据库. 碰到初始化日期需要写入ORACLE数据库NULL值的问题
本文主要介绍SLT写入ORACLE数据库NULL值的方法
本文主要处理SAP空日期写入ORACLE数据库NULL值.其它类型可以参照实现
日期写入ORACLE还需注意,详见连接
无峰,公众号:ABAP 技巧与实战问题解决 SLT同步日期格式字段
二
日期初始值
SAP中日期的初始值 00000000 ,该日期无法写入ORACLE的日期字段. 用户希望当SAP日期是初始值时, 写入ORACLE日期字段NULL值.
日期00000000 SLT传输到目标日期类型的字段中时,报错 DMC_RT_MSG167
通过SM21查找具体SQL错误 ORA-01843 NOT A VALID MONTH
三
关于NULL值
ABAP中NULL值的相关信息
ABAP环境的数据定义不允许NULL值
SE11定义数据字典时, 有个初始值选项. 但似乎勾选无效
通过显示表的数据库对象, 可以看到, 不管数据字典SE11中是否勾选初始值, 产生的HANA数据库中的字段都不支持初始值
ABAP中NULL值的产生原因一
一般情况下,视图LEFT JOIN 的右表,如果没有记录, 会生成NULL值, 此时需要通过语句 IS NULL 去访问
比如EXORD 是关联右表的字段. EXORD IS NULL ,通过 EXORD = '' 无法访问到数据
视图中消除NULL值
可以通过 COALESCE(spfli.distance, 0) as distance_with_default 设置NULL的默认值
ABAP中NULL值产生原因二
表新增了字段,新增字段的内容系统设置为NULL值.
四
SLT对NULL值的支持
如NOTES 2684480所述, SLT不支持从 SAP system 同步到ORACLE 表字段NULL值
五
分析
SLT究其根本, 有如下处理逻辑:
获取源系统的数据变化
从源系统读取变化的数据
写入目标系统
尝试找到写入目标系统的代码,研究调整使其支持写入NULL值即可.
六
方法
通过修改SLT写入程序实现NULL值写入ORACLE数据库
找到SLT配置的同步表(TCODE: LTRC)
双击查看生成的对象(需要注意的是: SLT初始化过程生成一个函数组,初始化后会删除之前的函数组,生成一个新的函数组. 最好源表数据为空, 让SLT快速初始化通过,生成最终执行的函数组, 在这个函数组的写入函数中修改代码. 下面的代码修改也支持初始化的函数组中的函数修改. 源表数据非空的情况下,需要两次修改函数)
对应的函数代码.
这里区分了 写入,更新,删除三个部分.图示了更新部分
同义词
这里有个奇怪的地方: 同步的表 ZTTS_SLT . ORACLE中也创建了ZTTS_SLT . 但是生成的函数中写入的表是 /1LT/10000000389
'/1LT/10000000389' 是表的ZTTS_SLT的同义词. SLT 为了使用OPEN SQL 更新ORACLE表, 采用了同义词. 该同义词在SE11中存在表,但是数据库定义不同于SE11定义
如下图是 SE11(SLT中) 定义 和 SLT数据库中定义 及 ORACLE表定义的区别. 看起来SLT定义同义词的目的是为了使用OPEN SQL语句操作目标ORACLE数据库表
七
技术难点
ORACLE支持的最小日期是 00010101 . 尝试写入00000000会报错.
因此调整标准代码中写入,更新部分的内容00000000->00010101
标准程序写入数据后, 再把所有00010101的字段通过NATIVE SQL 更新成 NULL值.
思路比较简单,但具体实现NATIVE SQL 执行动态SQL却费了很大的功夫
最终发现, 只有通过调用存储过程, 才能实现动态UPDATE 语句
过程如下
1. ORACLE中创建存储过程
CREATE OR REPLACE PROCEDURE execute_dynamic_sql (
p_sql_statement IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE p_sql_statement;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END execute_dynamic_sql;
2. ABAP中动态生成SQL语句,调用存储过程
八
为何不使用ADBC
NATIVE SQL动态更新数据碰到了很多麻烦. 在无数次和AI的沟通中, AI都建议使用ADBC实现动态语句更新数据(实现简单)
但因为标准生成的代码
insert /1lt/10000000389 client specified connection (con_name) 已经创建了一个数据库连接.
commit connection (con_name).
NATIVE SQL 可以直接使用这个连接
exec sql.
SET CONNECTION :con_NAME
endexec.
但是ADBC需要创建自己的连接.
在提交前创建连接会失败.
但如果放到提交后再更新. 更新为NULL值的语句和写入语句就不再一个事务中. 这样会导致其它系统可能会读取到 00010101 的初始化日期.
九
最终实现
封装类 ZCL_SLT_WRITE_DATUM_NULL
类构造方法中读取传入表的字段
CONVERT_DATUM方法负责动态把所有日期字段为00000000的转换成 00010101
UPDATE_NULL 负责在标准程序提交前,构造动态SQL把日期00010101更新成NULL值
标准写入函数的修改
在函数开始时替换传入的数据内容,需要考虑替换写入表和更新表
在提交数据库之前先更新数据为NULL
十
实测通过
测试表ZTTS_SLT中新增或修改的记录的空日期,写入ORACLE系统值为NULL
十一
总结
SLT把初始化日期写入目标ORACLE数据库NULL值,无法通过标准功能实现. 需要通过重写SLT生成的数据库写入函数. 因为该函数在配置LTRC时生成,并且生成的函数名可能与开发系统不一致. 所以需要在生产系统中直接修改函数,添加相关代码实现更新NULL值
SLT生成的写入目标数据库的代码没有使用ADBC的方法. 可能的原因
性能考虑, NATIVE SQL 性能可能好于ADBC
版本原因,SLT最初版的产品可能还无法使用ADBC
感觉可以在这里改进一下, 改用ADBC,同时预留出口, 以便客户实现更多样化的目标数据库内容写入
SLT 初始化对象是会生成一个函数组.初始化后会生成另外一个函数组.
SLT全局类型映射可能会导致在目标数据库创建表失败. (不确定这个BUG新版本是否修复). 最好使用表+字段级别的类型映射
THE
END
约定
如果你对这篇文章感兴趣,请帮忙点赞,在看,分享.
请微信联系管理员:
syjf1976
sharry_xlp
Yannick_Duan
申请进入公众号讨论群提问或者参与话题讨论