SAP 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方式连接外部数据库的验证失败. 不推荐使用. 其它三种方式根据实际情况选择使用就好.

示例代码详见文末.

示例代码,

*&---------------------------------------------------------------------*
*& 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*S4QWHERE 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 mtdINTO 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 :connENDEXEC.IF sy-subrc <> 0. "如果连接没有打开, 打开连接EXEC SQL.CONNECT TO :connENDEXEC.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 :CONNENDEXEC.*输出结果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 FORSELECT zztno,werks FROM zttempENDEXEC."循环通过游标读取记录" 两种赋值方式:" 1.按字段顺序赋值,select 字段与 INTO 字段顺序必须一致"   FETCH NEXT dbcur INTO :ls_TEMP-ZZTNO,:LS_TEMP-WERKS" 2.按结构整体赋值:select 字段必须与结构字段顺序一致,且字段长度一致."   FETCH NEXT dbcur INTO :ls_TEMPDO.EXEC SQL.FETCH NEXT dbcur INTO :ls_TEMP-ZZTNO,:LS_TEMP-WERKSENDEXEC.IF sy-subrc <> 0.EXIT.ELSE.APPEND ls_temp TO lt_temp.ENDIF.ENDDO."关闭游标EXEC SQL.CLOSE dbcurENDEXEC.gt_temp[] = lt_temp[].
ENDFORM.
*&---------------------------------------------------------------------*
*& Form FRM_METHOD_2
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& -->  p1        text
*& <--  p2        text
*&---------------------------------------------------------------------*
FORM frm_method_2 .conn = 'MTD'."检查连接是否已经打开EXEC SQL.SET CONNECTION :connENDEXEC.IF sy-subrc <> 0. "如果连接没有打开, 打开连接EXEC SQL.CONNECT TO :connENDEXEC.ENDIF.
*注意:工作区 gs_temp 内表 gt_temp 必须是全局变量EXEC SQL PERFORMING FRM_FILL_DATA.SELECT zztno,werks FROM zttemp INTO :GS_TEMPENDEXEC.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_WERKSWHERE WERKS = '1003'ENDEXEC.IF sy-subrc = 0.
*提交数据更新EXEC SQL.COMMIT WORKENDEXEC.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 WORKENDEXEC.DATA: lv_msg(50).lv_msg = '写入成功记录数:' && sy-dbcnt .cl_demo_output=>display(  lv_msg ).ELSE.cl_demo_output=>display( '写入失败' ).ENDIF.
ENDFORM.

*&---------------------------------------------------------------------*
*& 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_structdescrp_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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/132572.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言练习58:⾃定义类型:结构体

⾃定义类型&#xff1a;结构体 结构体的概念 结构是⼀些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 结构体是一个种自定义的数据类型&#xff0c;它可以由很多个默认数据类型组成。它主要用于描述复杂场景下的变量。 例如&#xff0c;想…

前端深入理解JavaScript中的WeakMap和WeakSet

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 1. WeakMap和WeakSet概述 1.1 WeakMap 1.2 WeakSet 2. WeakMap深入解析 2.1 WeakMap的创建和使用 2.2 WeakMap…

软件流程图怎么画?详细画法看这里

软件流程图怎么画&#xff1f;软件流程图是软件开发过程中必不可少的一环&#xff0c;可以帮助开发人员更好地理解和规划软件开发的流程。在制作软件流程图的时候&#xff0c;我们可以使用一些制作工具。下面就给大家介绍一款好用的绘制工具。 我们可以使用【迅捷画图】来进行流…

Android端Base64解码表情emoj乱码

一、背景&#xff1a;H5端用户评论中包含表情包&#xff0c;通过JSBridge 传递给客户端&#xff0c;Android Base64解码之后&#xff0c;显示乱码&#xff08;是菱形问号&#xff09;。小程序和iOS可以正常解码出表情。用Base64在线编码解码&#xff08;Base64 在线编码解码 | …

BOM操作

文章目录 BOM事件页面加载调整窗口事件定时器停止计时器Location对象History对象Offsetleft获取元素偏移Offset与style的区别可视区client系列滚动scroll系列Mouseover和mousenter区别 动画原理实现动画封装给不同对象添加定时器缓动动画原理多个位置间移动 BOM事件 页面加载 …

BeanUtils.copyProperties的使用场景

1. 常见场景 我们如果有两个具有很多相同属性名的JavaBean对象a和b&#xff0c;想把a中的属性赋值到b&#xff0c;例如 接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象 传统做法是手动set&#xff0c;即 XxxBean xxxBea…

Python Opencv实践 - HoG特征计算

参考资料&#xff1a;https://www.cnblogs.com/alexme/p/11361563.html https://blog.csdn.net/qq_43348528/article/details/108638030 import cv2 as cv import numpy as np import matplotlib.pyplot as plt from skimage import exposure from skimage.feature i…

靶场溯源第二题

关卡描述&#xff1a;1. 网站后台登陆地址是多少&#xff1f;&#xff08;相对路径&#xff09; 首先这种确定的网站访问的都是http或者https协议&#xff0c;搜索http看看。关于http的就这两个信息&#xff0c;然后172.16.60.199出现最多&#xff0c;先过滤这个ip看看 这个很…

SSM - Springboot - MyBatis-Plus 全栈体系(八)

第二章 SpringFramework 四、SpringIoC 实践和应用 4. 基于 配置类 方式管理 Bean 4.4 实验三&#xff1a;高级特性&#xff1a;Bean 注解细节 4.4.1 Bean 生成 BeanName 问题 Bean 注解源码&#xff1a; public interface Bean {//前两个注解可以指定Bean的标识AliasFor…

Mendeley在linux中无法打开APPimage

原因:FUSE 库为用户空间程序提供了一个接口&#xff0c;可以将虚拟文件系统导出到 Linux 内核。由于缺少这个关键库&#xff0c;AppImage 无法按预期工作。 1 安装fuse,打开终端,输入命令 sudo apt install libfuse2 输入用户密码结,果如下 2 确保APPimage作为程序运行 右击…

docker 获取Nvidia 镜像 | cuda |cudnn

本文分享如何使用docker获取Nvidia 镜像&#xff0c;包括cuda10、cuda11等不同版本&#xff0c;cudnn7、cudnn8等&#xff0c;快速搭建深度学习环境。 1、来到docker hub官网&#xff0c;查看有那些Nvidia 镜像 https://hub.docker.com/r/nvidia/cuda/tags?page2&name11.…

【计算机视觉 | 图像模型】常见的计算机视觉 image model(CNNs Transformers) 的介绍合集(五)

文章目录 一、MoCo v3二、AmoebaNet三、Residual Multi-Layer Perceptrons四、FractalNet五、LV-ViT六、RepVGG七、Transformer in Transformer八、SimpleNet九、SpineNet十、Bottleneck Transformer十一、ZFNet十二、DetNet十三、Invertible Rescaling Network十四、SNet十五、…

SVN 索引版本与打包版本号不匹配

今天突然遇到了一个问题&#xff0c;SVN上传不了&#xff0c;错误提示如下&#xff1a; 解决方法&#xff1a; 1.其实&#xff0c;这是SVN库不小心搞坏了&#xff0c;只能重新再创建一个SVN仓库了。

Redis:分布式锁误删原因分析

一、线程阻塞 例如&#xff0c;线程一获取分布式锁&#xff0c;但是线程一阻塞时间过长&#xff0c;导致锁超时释放。此时线程二获取分布式锁。当线程一阻塞结束后&#xff0c;释放分布式锁&#xff0c;但是释放的却是线程二的锁。此时线程二就不安全了&#xff0c;线程三也可…

Linux下修改jar包中的配置文件application.conf

文件位置 jar包文件工程目录 打包后解压jar包目录 提取和上传 jar tf XXX.jar # 获取包内文件 application.conf是jar包的配置文件&#xff0c;如果修改需要 提取文件 jar xf my-app.jar application.conf 修改后上传文件 jar uf my-app.jar application.conf

解决开了burp suite ,火狐访问不了其他网站的问题

问题描述&#xff1a; 有软件正在阻止 Firefox 安全地连接至此网站 www.baidu.com 很像是一个安全&#xff08;连接加密&#xff09;的网站&#xff0c;但我们未能与它建立安全连接。这个问题是由 PortSwigger CA 所造成&#xff0c;它是您的计算机或您所在网络中的软件。 您…

为什么Proteus串口无法正常显示

我以前就可以正常显示&#xff0c;但是最近一段时间&#xff0c;发现串口无法正常显示&#xff0c;试了很多办法都不行&#xff0c; 然后今天干好有点时间就刷了个机&#xff0c;然后居然就好了&#xff0c; 这就说明&#xff1a;Proteus不正常可能是病毒破坏了某个文件导致异…

HSRP(热备份路由选择协议)的概念,原理与配置实验

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 梦想从未散场&#xff0c;传奇永不落幕&#xff0c;持续更新优质网络知识、Python知识、Linux知识以及各种小技巧&#xff0c;愿你我共同在CSDN进步 目录 一、了解HSRP协议 1. 什么是HSRP协议 2、HSRP协议的…

java和fastjson

1.java是如何跨平台通信的 java--->class字节码--->jvm虚拟机运行 2.使因为jvm只会读文件名 如果不一致 则无法找到文件 3.main 函数说明java代码的接口 被使用 4.java和class后缀的区别 java是当前编写的代码文件 class是编译后的文件 5.void 没有返回值 这…

Django系列:Django简介与MTV架构体系概述

Django系列 Django简介与MTV架构体系概述 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/132890054 【介…