技术标签: python java 编程语言 mysql 数据库
点击蓝字 关注我们
一
前言
前文讲解了怎么配置连接外部数据库
详见链接
无峰,公众号:ABAP 技巧与实战ABAP基础知识 访问外部数据库-配置篇
本文主要介绍通过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工具箱之一键生成报表
该工具在付费文章中可以获取.
文中通过AMDP方式连接外部数据库的验证失败. 不推荐使用. 其它三种方式根据实际情况选择使用就好.
示例代码详见文末.
THE
END
约定
如果你对这篇文章感兴趣,请帮忙点赞,在看,分享.
(如果你真的喜欢这篇文章,请记得回来打个赏,作为支持我继续下去的动力,这是一个正反馈过程. 越多的人打赏,作者越有动力分享,读者就能享受更多的福利.毕竟打赏的金额富不了我,穷不了你,却能支持这个公众号长久发文.)
公众号 : syjf1976_abap
ABAP开发技巧
微信号 : 392077
请微信联系管理员:
syjf1976
sharry_xlp
Yannick_Duan 申请进入公众号讨论群
示例代码,OPEN SQL:
*&---------------------------------------------------------------------*
*& 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 字段必须与结构字段顺序一致,且字段长度一致.
" FETCH NEXT dbcur INTO :ls_TEMP
DO.
EXEC SQL.
FETCH NEXT dbcur INTO :ls_TEMP-ZZTNO,:LS_TEMP-WERKS
ENDEXEC.
IF sy-subrc <> 0.
EXIT.
ELSE.
APPEND ls_temp TO lt_temp.
ENDIF.
ENDDO.
"关闭游标
EXEC SQL.
CLOSE dbcur
ENDEXEC.
gt_temp[] = lt_temp[].
ENDFORM.
*&---------------------------------------------------------------------*
*& Form FRM_METHOD_2
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_method_2 .
conn = 'MTD'.
"检查连接是否已经打开
EXEC SQL.
SET CONNECTION :conn
ENDEXEC.
IF sy-subrc <> 0. "如果连接没有打开, 打开连接
EXEC SQL.
CONNECT TO :conn
ENDEXEC.
ENDIF.
*注意:工作区 gs_temp 内表 gt_temp 必须是全局变量
EXEC SQL PERFORMING FRM_FILL_DATA.
SELECT zztno,werks FROM zttemp INTO :GS_TEMP
ENDEXEC.
ENDFORM.
FORM frm_fill_data.
APPEND gs_temp TO gt_temp.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form FRM_UPDATE
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_update .
DATA: lv_werks(4).
lv_werks = '1002'.
EXEC SQL.
UPDATE ZTTEMP SET WERKS = :LV_WERKS
WHERE WERKS = '1003'
ENDEXEC.
IF sy-subrc = 0.
*提交数据更新
EXEC SQL.
COMMIT WORK
ENDEXEC.
DATA: lv_msg(50).
lv_msg = '更新成功记录数:' && sy-dbcnt .
cl_demo_output=>display( lv_msg ).
ELSE.
cl_demo_output=>display( '更新失败' ).
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form FRM_INSERT
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_insert .
DATA: lv_werks(4).
lv_werks = '1002'.
EXEC SQL.
INSERT INTO ZTTEMP VALUES ('4502',:LV_WERKS)
ENDEXEC.
IF sy-subrc = 0.
*提交数据更新
EXEC SQL.
COMMIT WORK
ENDEXEC.
DATA: lv_msg(50).
lv_msg = '写入成功记录数:' && sy-dbcnt .
cl_demo_output=>display( lv_msg ).
ELSE.
cl_demo_output=>display( '写入失败' ).
ENDIF.
ENDFORM.
示例代码 ADBC
*&---------------------------------------------------------------------*
*& Report ZTS_DBCO_ADBC
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zts_dbco_adbc.
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.
DATA: gv_sql TYPE string.
START-OF-SELECTION.
gv_sql = 'SELECT zztno,werks FROM zttemp'.
PERFORM frm_get_data_adbc_simple.
* PERFORM frm_get_data_adbc.
cl_demo_output=>display( gt_temp ).
*&---------------------------------------------------------------------*
*& Form FRM_GET_DATA_ADBC
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_get_data_adbc .
DATA: r_adbc_conn TYPE REF TO cl_sql_connection,
r_adbc_query TYPE REF TO cl_sql_statement,
r_metadata TYPE REF TO data,
it_metadata TYPE adbc_rs_metadata_descr_tab,
lv_len TYPE i,
lv_off TYPE i,
wa_metadata LIKE LINE OF it_metadata,
r_adbc_result TYPE REF TO cl_sql_result_set,
r_tabletype TYPE REF TO cl_abap_tabledescr,
r_cxadbc TYPE REF TO cx_dba_adbc,
r_cxsql TYPE REF TO cx_sql_exception,
tabix_n(4) TYPE n,
column_names TYPE HASHED TABLE OF adbc_name WITH UNIQUE KEY table_line.
DATA: lv_stmt_type TYPE string.
DATA:
ex_structdescr TYPE REF TO cl_abap_structdescr,
ex_result_ref TYPE REF TO data.
*获取sql语句的类型
lv_stmt_type = cl_hdb_sql_executor=>get_statement_type( gv_sql ).
*创建默认数据库的链接对象
r_adbc_conn = cl_db6_con=>get_connection( 'MTD' ).
*创建一个查询对象
r_adbc_query = r_adbc_conn->create_statement( ).
*基于sql语句创建一个结果对象
r_adbc_result = r_adbc_query->execute_query( gv_sql ).
*获取结果集合的字段名
it_metadata = r_adbc_result->get_metadata( ).
*使用结果集合的字段信息,创建一个数据对象-结构
r_metadata = r_adbc_result->get_struct_ref( md_tab = it_metadata p_strict = abap_false ).
*创建一个数据对象-内表
ex_structdescr ?= cl_abap_typedescr=>describe_by_data_ref( r_metadata ).
r_tabletype = cl_abap_tabledescr=>create( p_line_type = ex_structdescr
p_table_kind = cl_abap_tabledescr=>tablekind_std ).
CREATE DATA ex_result_ref TYPE HANDLE r_tabletype.
*传递结果集一个数据对象-内表
r_adbc_result->set_param_table( itab_ref = ex_result_ref ).
*获取数据内容
r_adbc_result->next_package( EXPORTING upto = 100 ).
*关闭连接
r_adbc_result->close( ).
*赋值数据到内表
FIELD-SYMBOLS: <fs_itab> TYPE STANDARD TABLE.
ASSIGN ex_result_ref->* TO <fs_itab>.
MOVE-CORRESPONDING <fs_itab> TO gt_temp.
ENDFORM.
FORM frm_get_data_adbc_simple .
DATA: r_adbc_conn TYPE REF TO cl_sql_connection,
r_adbc_query TYPE REF TO cl_sql_statement,
r_metadata TYPE REF TO data,
it_metadata TYPE adbc_rs_metadata_descr_tab,
lv_len TYPE i,
lv_off TYPE i,
wa_metadata LIKE LINE OF it_metadata,
r_adbc_result TYPE REF TO cl_sql_result_set,
r_tabletype TYPE REF TO cl_abap_tabledescr,
r_cxadbc TYPE REF TO cx_dba_adbc,
r_cxsql TYPE REF TO cx_sql_exception,
tabix_n(4) TYPE n,
column_names TYPE HASHED TABLE OF adbc_name WITH UNIQUE KEY table_line.
DATA: lv_stmt_type TYPE string.
DATA:
ex_structdescr TYPE REF TO cl_abap_structdescr,
ex_result_ref TYPE REF TO data.
*创建默认数据库的链接对象
r_adbc_conn = cl_db6_con=>get_connection( 'MTD' ).
*创建一个查询对象
r_adbc_query = r_adbc_conn->create_statement( ).
*基于sql语句创建一个结果对象
r_adbc_result = r_adbc_query->execute_query( gv_sql ).
*定义
DATA: lr_ref LIKE REF TO gt_temp.
CREATE DATA lr_ref .
*传递结果集一个数据对象-内表
r_adbc_result->set_param_table( itab_ref = lr_ref ).
*获取数据内容
r_adbc_result->next_package( EXPORTING upto = 100 ).
*关闭连接
r_adbc_result->close( ).
*赋值数据到内表
gt_temp = lr_ref->*.
ENDFORM.
文章浏览阅读1.2k次,点赞35次,收藏18次。AutowiredPostConstruct 注释用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。此方法必须在将类放入服务之前调用。支持依赖关系注入的所有类都必须支持此注释。即使类没有请求注入任何资源,用 PostConstruct 注释的方法也必须被调用。只有一个方法可以用此注释进行注释。_springboot2.7获取bean
文章浏览阅读2.1k次。理论介绍 节点定义package logistic;public class Instance { public int label; public double[] x; public Instance(){} public Instance(int label,double[] x){ this.label = label; th_logisticregression java
文章浏览阅读981次,点赞21次,收藏18次。本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。下面我们来进行文件的恢复,执行下文中的lsof命令,在其返回结果中我们可以看到test-recovery.txt (deleted)被删除了,但是其存在一个进程tail使用它,tail进程的进程编号是1535。我们看到文件名为3的文件,就是我们刚刚“误删除”的文件,所以我们使用下面的cp命令把它恢复回去。命令进入该进程的文件目录下,1535是tail进程的进程id,这个文件目录里包含了若干该进程正在打开使用的文件。
文章浏览阅读10w+次,点赞12次,收藏72次。RTMP(Real Time Messaging Protocol)实时消息传输协议是Adobe公司提出得一种媒体流传输协议,其提供了一个双向得通道消息服务,意图在通信端之间传递带有时间信息得视频、音频和数据消息流,其通过对不同类型得消息分配不同得优先级,进而在网传能力限制下确定各种消息得传输次序。_rtmp
文章浏览阅读64次。2017年12月的计算机等级考试将要来临!出国留学网为考生们整理了2017年12月计算机一级MSOffice考试习题,希望能帮到大家,想了解更多计算机等级考试消息,请关注我们,我们会第一时间更新。2017年12月计算机一级MSOffice考试习题(二)一、单选题1). 计算机最主要的工作特点是( )。A.存储程序与自动控制B.高速度与高精度C.可靠性与可用性D.有记忆能力正确答案:A答案解析:计算...
文章浏览阅读356次。在学MYSQL的时候刚刚好看到了这个提权,很久之前用过别人现成的,但是一直时间没去细想, 这次就自己复现学习下。 0x00 UDF 什么是UDF? UDF (user defined function),即用户自定义函数。是通过添加新函数,对MySQL的功能进行扩充,就像使..._the provided input file '/usr/share/metasploit-framework/data/exploits/mysql
文章浏览阅读3.1w次,点赞71次,收藏485次。webService一 WebService概述1.1 WebService是什么WebService是一种跨编程语言和跨操作系统平台的远程调用技术。Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准...
文章浏览阅读1w次。前言照例给出官网:Retrofit官网其实大家学习的时候,完全可以按照官网Introduction,自己写一个例子来运行。但是百密一疏,官网可能忘记添加了一句非常重要的话,导致你可能出现如下错误:Could not locate ResponseBody converter错误信息:Caused by: java.lang.IllegalArgumentException: Could not l_已添加addconverterfactory 但是 could not locate responsebody converter
文章浏览阅读1k次。一套键鼠控制Windows+Linux——Synergy在Windows10和Ubuntu18.04共控的实践Synergy简介准备工作(重要)Windows服务端配置Ubuntu客户端配置配置开机启动Synergy简介Synergy能够通过IP地址实现一套键鼠对多系统、多终端进行控制,免去了对不同终端操作时频繁切换键鼠的麻烦,可跨平台使用,拥有Linux、MacOS、Windows多个版本。Synergy应用分服务端和客户端,服务端即主控端,Synergy会共享连接服务端的键鼠给客户端终端使用。本文_linux 18.04 synergy
文章浏览阅读374次。写demo的时候遇到了很多问题,记录一下。安装nacos1.4.0配置mysql数据库,新建nacos_config数据库,并根据初始化脚本新建表,使配置从数据库读取,可单机模式启动也可以集群模式启动,启动时 ./start.sh -m standaloneapplication.properties 主要是db部分配置## Copyright 1999-2018 Alibaba Group Holding Ltd.## Licensed under the Apache License,_seata1.4.0 +nacos 集成
文章浏览阅读833次。iperf使用方法详解 iperf3是一款带宽测试工具,它支持调节各种参数,比如通信协议,数据包个数,发送持续时间,测试完会报告网络带宽,丢包率和其他参数。 安装 sudo apt-get install iperf3 iPerf3常用的参数: -c :指定客户端模式。例如:iperf3 -c 192.168.1.100。这将使用客户端模式连接到IP地址为192.16..._iperf客户端指定ip地址
文章浏览阅读7.4k次。 写这个函数目的不是为了和C/C++库中的函数在性能和安全性上一比高低,只是为了给那些喜欢探讨函数内部实现的网友,提供一种从浮点性到字符串转换的一种途径。 浮点数是有精度限制的,所以即使我们在使用C/C++中的sprintf或者cout 限制,当然这个精度限制是可以修改的。比方在C++中,我们可以cout.precision(10),不过这样设置的整个输出字符长度为10,而不是特定的小数点后1_c++浮点数 转 字符串 精度损失最小