原创 无峰 [ ABAP 技巧与实战 ](javascript:void\(0\);)
**点击蓝字** 关注我们
一
前言
前文讲解了怎么配置连接外部数据库
> 详见链接 无峰,公众号:ABAP 技巧与实战 [ ABAP基础知识 访问外部数据库-配置篇
> ](http://mp.weixin.qq.com/s?__biz=Mzg4MjA1MjE4NQ==&mid=2247486675&idx=1&sn=880947996ba5d9e851b666934c5d2f97&chksm=cf5dd1e5f82a58f3f4dfa774156a3853dcac4fc3950944ace75517dd689eceee2ca9aab19cb4#rd)
本文主要介绍通过ABAP语言访问外部数据库的几种方式
二
外部数据库配置
本文示例中的代码访问了两个外部数据库
MTD : 外部oracle数据库,其中示例表 ZTTEMP 字段( ZZTNO,WERKS)
S4Q : 外部HANA数据库(开发系统访问测试系统的数据库), 使用表USR02,ZTTEMP
三
ABAP访问外部数据库
通过ABAP访问外部数据库有四种方式.根据不同的情况,可以选择不同的方法.
• OPEN SQL访问
• NATIVE SQL 访问
• ADBC(ABAP Database Connectivity)
• AMDP ABAP Managed Database Procedures ? (未验证通过)
四
OPEN SQL直接访问
OPEN SQL 访问的限制条件:必须在ABAP数据字典中存在该表名,并且最好同目标系统表结构一致,
一般情况下,用来访问另外一个同版本的ECC数据库.当然,也可以把ECC的表定义语句在目标系统中创建一个同名同结构的表,然后用该方式访问.
直接访问时,在FROM TABLE 后面添加 CONNECTION s4q .
s4q是DBCO中建立的和另外一个S/4系统的连接
_**01** _
**报错及处理一**
可能的报错及处理方式
下图报错的原因是访问ORACLE数据库必须指定一个SCHEMA. 这个可以配置在连接参数中.
_**01** _
**报错及处理二**
出现下面的报错,表示系统尝试使用MANDT限制数据, 此时需要给OPEN SQL 语句添加CLIENT SPECIFIED 强制OPEN SQL
不要补充MANDT限制
五
NATIVE SQL访问
通过NATIVE SQL 访问外部数据库步骤
• 打开连接
• 执行SQL命令
• 关闭连接
_示例代码见文末_
_**01** _
**读取多条记录的方式**
• 游标方式 图一
• 非游标方式 图二
非游标方式其实隐式使用了游标.性能比游标方式要差.数据量小的时候看不出来. 大量数据读取就能看出二者的性能差异了.
图三是标准帮助中提示的优劣比较.
图一
图二
图三
六
ADBC访问
ADBC(ABAP Database Connectivity) 是SAP提供的原生SQL(Native
SQL)接口API.可以通过ADBC执行任何数据库的原生SQL语句.
ABAP中有个标准的DEMO程序: ADBC_DEMO 演示了各种SQL语句的调用方式.图四的代码示例给出了SELECT语句的常用写法.
大概需要如下的过程
• 创建默认数据库的链接对象
• 创建一个查询对象
• 基于sql语句创建一个结果对象
• 定义传递结果集 一个数据对象-内表
• 获取数据内容
• 关闭连接
• 赋值数据到内表
_示例代码详见文末_
七
通过AMDP 访问?
AMDP ABAP Managed Database Procedures
标准ABAP帮助体系中提到访问外部数据库的方法中还有一种AMDP方式.为了完善本文,补充了一个AMDP访问外部数据库表的示例.(验证未通过)
尝试中发现AMDP只适用与HANA数据库. 并且尝试通过DEMO程序 DEMO_AMDP_CONNECTION 连接外部数据库S4Q. 尝试失败.
(报错见图四)
感觉AMDP 并不支持连接外部数据库.
图五中示例代码的连接 是 R/3*开头. 帮助中说S/3*是SERVICE CONNECT. 但是不理解有什么用处.
图四
图五
图六
八
总结
ABAP访问外部数据库的几种方式中. OPEN SQL 最简单,但是有很大局限性. NATIVE方式最简单易懂. 性能也最好. 但是不利于代码动态化.
ADBC 可以动态的实现数据的读取及内表的赋值.
前文DB02 SQL编辑器SQL语句自动生成报表 就采用了ADBC访问数据库的方法: 根据语句动态定义选择屏幕,动态定义内表, 读取的数据写入内表呈现.
> 详见链接 无峰,公众号:ABAP开发技巧 [ SAP工具箱之一键生成报表
> ](https://mp.weixin.qq.com/s?__biz=Mzg4MjA1MjE4NQ==&mid=2247484616&idx=1&sn=0ace84b7fb5a0faf1e468d7530538ef8&chksm=cf5dd9fef82a50e81f5d26d7a4cef2555d6ee5439428e25b554fa03363426dee1bfc26fdef56&token=1596155933&lang=zh_CN#rd)
该工具在付费文章中可以获取.
文中通过AMDP方式连接外部数据库的验证失败. 不推荐使用. 其它三种方式根据实际情况选择使用就好.
示例代码详见文末.
** THE **
** END **
**约定**
如果你对这篇文章感兴趣,请帮忙点赞,在看,分享.
( **如果你真的喜欢这篇文章,请记得回来打个赏,作为支持我继续下去的动力,这是一个正反馈过程.
越多的人打赏,作者越有动力分享,读者就能享受更多的福利.毕竟打赏的金额富不了我,穷不了你,却能支持这个公众号长久发文.** )
**公众号 : syjf1976_abap**
**ABAP开发技巧**
**微信号 : 392077**
syjf1976
sharry_xlp
Yannick_Duan 申请进入公众号讨论群
*&---------------------------------------------------------------------**& Report ZTS_SQL_DBCO*&---------------------------------------------------------------------**&*&---------------------------------------------------------------------*REPORT zts_dbco_opensql.
PARAMETERS: p_s4 RADIOBUTTON GROUP ra1, p_ora RADIOBUTTON GROUP ra1.
START-OF-SELECTION. CASE 'X'. WHEN p_s4.*访问另一个S4系统的同名表*需要注意的是,目标系统CLIENT和当前CLIENT 很可能不一样, 所以需要加上CLIENT SPECIFIED 避免CLIENT不同干扰数据获取 SELECT * FROM usr02 CLIENT SPECIFIED INTO TABLE @DATA(lt_usr02) BYPASSING BUFFER CONNECTION R/3*S4Q WHERE bname = '00177'. ."可以获取数据 DATA(lv_subrc) = sy-subrc . cl_demo_output=>write( lv_subrc ). cl_demo_output=>write( lt_usr02 ). cl_demo_output=>display( ). WHEN p_ora.*访问另一个其它系统的同名表*如果ABAP表有MANDT , 目标表没有, 则需要添加CLIENT SPECIFIED 避免系统自动添加MANDT 的限制条件,导致报错:字段MANDT不存在 DATA: BEGIN OF ls_temp, zztno(30), werks(4), END OF ls_temp. DATA: lt_temp LIKE TABLE OF ls_temp.
SELECT zztno,werks FROM zttemp CLIENT SPECIFIED CONNECTION mtd INTO TABLE @lt_temp.
cl_demo_output=>display( lt_temp ). ENDCASE.* COMMIT CONNECTION s4q. "在连接中提交.示例代码 NATIVESQL
*&---------------------------------------------------------------------**& Report ZTS_SQL_DBCO*&---------------------------------------------------------------------**&*&---------------------------------------------------------------------*REPORT zts_dbco_nativesql.
PARAMETERS: p_1 RADIOBUTTON GROUP ra1, p_2 RADIOBUTTON GROUP ra1, p_3 RADIOBUTTON GROUP ra1, p_4 RADIOBUTTON GROUP ra1.
DATA: BEGIN OF gs_temp, zztno(30), werks(4), END OF gs_temp.DATA: gt_temp LIKE TABLE OF gs_temp.DATA conn TYPE dbcon-con_name.INITIALIZATION. %_p_1_%_app_%-text = 'SELECT方式一:DO循环读取游标,添加内表'. %_p_2_%_app_%-text = 'SELECT方式二:通过例程添加内表'. %_p_3_%_app_%-text = 'UPDATE:更新表内容'. %_p_4_%_app_%-text = 'INSERT:写入表内容'.
START-OF-SELECTION. conn = 'MTD'. "检查连接是否已经打开 EXEC SQL. SET CONNECTION :conn ENDEXEC. IF sy-subrc <> 0. "如果连接没有打开, 打开连接 EXEC SQL. CONNECT TO :conn ENDEXEC. ENDIF.
*两种方式: 方式一性能好于方式二 CASE 'X'. WHEN p_1. PERFORM frm_method_1. "SELECT方式一:DO循环读取游标,添加内表'. WHEN p_2. PERFORM frm_method_2. "SELECT方式二:通过例程添加内表'. when p_3. perform frm_update. when p_4. perform frm_insert. ENDCASE.
"关闭数据库连接 EXEC SQL. DISCONNECT :CONN ENDEXEC.
*输出结果 CASE 'X'. WHEN p_1 or p_2. cl_demo_output=>display( gt_temp ). ENDCASE.*&---------------------------------------------------------------------**& Form FRM_METHOD_1*&---------------------------------------------------------------------**& text*&---------------------------------------------------------------------**& --> p1 text*& <-- p2 text*&---------------------------------------------------------------------*FORM frm_method_1 . DATA: ls_temp LIKE gs_temp, lt_temp LIKE TABLE OF ls_temp. "执行SQL语句:通过open dbcur打开游标 EXEC SQL. OPEN dbcur FOR SELECT zztno,werks FROM zttemp ENDEXEC. "循环通过游标读取记录 " 两种赋值方式: " 1.按字段顺序赋值,select 字段与 INTO 字段顺序必须一致 " FETCH NEXT dbcur INTO :ls_TEMP-ZZTNO,:LS_TEMP-WERKS " 2.按结构整体赋值:select 字段必须与结构字段顺序一致,且字段长度一致. "...