ABAP基础知识 访问外部数据库-开发篇-程序员宅基地

技术标签: 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.
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/syjf1976/article/details/120320522

智能推荐

Linux查看登录用户日志_怎么记录linux设备 发声的登录和登出-程序员宅基地

文章浏览阅读8.6k次。一、Linux记录用户登录信息文件1  /var/run/utmp----记录当前正在登录系统的用户信息;2  /var/log/wtmp----记录当前正在登录和历史登录系统的用户信息;3  /var/log/btmp:记录失败的登录尝试信息。二、命令用法1.命令last,lastb---show a listing of la_怎么记录linux设备 发声的登录和登出

第四章笔记:遍历--算法学中的万能钥匙-程序员宅基地

文章浏览阅读167次。摘要:1. 简介 2. 公园迷宫漫步 3. 无线迷宫与最短(不加权)路径问题 4. 强连通分量1. 简介在计算机科学裡,树的遍历(也称为树的搜索)是圖的遍歷的一种,指的是按照某种规则,不重复地访问某种樹的所有节点的过程。具体的访问操作可能是检查节点的值、更新节点的值等。不同的遍历方式,其访问节点的顺序是不一样的。两种著名的基本遍历策略:深度优先搜索(DFS) 和 广度优先搜索(B...

【案例分享】使用ActiveReports报表工具,在.NET MVC模式下动态创建报表_activereports.net 实现查询报表功能-程序员宅基地

文章浏览阅读591次。提起报表,大家会觉得即熟悉又陌生,好像常常在工作中使用,又似乎无法准确描述报表。今天我们来一起了解一下什么是报表,报表的结构、构成元素,以及为什么需要报表。什么是报表简单的说:报表就是通过表格、图表等形式来动态显示数据,并为使用者提供浏览、打印、导出和分析的功能,可以用公式表示为:报表 = 多样的布局 + 动态的数据 + 丰富的输出报表通常包含以下组成部分:报表首页:在报表的开..._activereports.net 实现查询报表功能

Ubuntu18.04 + GNOME xrdp + Docker + GUI_docker xrdp ubuntu-程序员宅基地

文章浏览阅读6.6k次。最近实验室需要用Cadence,这个软件的安装非常麻烦,每一次配置都要几个小时,因此打算把Cadence装进Docker。但是Cadence运行时需要GUI,要对Docker进行一些配置。我们实验室的服务器运行的是Ubuntu18.04,默认桌面GNOME,Cadence装进Centos的Docker。安装Ubuntu18.04服务器上安装Ubuntu18.04的教程非常多,在此不赘述了安装..._docker xrdp ubuntu

iOS AVFoundation实现相机功能_ios avcapturestillimageoutput 兼容性 ios17 崩溃-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏2次。首先导入头文件#import 导入头文件后创建几个相机必须实现的对象 /** * AVCaptureSession对象来执行输入设备和输出设备之间的数据传递 */ @property (nonatomic, strong) AVCaptureSession* session; /** * 输入设备 */_ios avcapturestillimageoutput 兼容性 ios17 崩溃

Oracle动态性能视图--v$sysstat_oracle v$sysstat视图-程序员宅基地

文章浏览阅读982次。按照OracleDocument中的描述,v$sysstat存储自数据库实例运行那刻起就开始累计全实例(instance-wide)的资源使用情况。 类似于v$sesstat,该视图存储下列的统计信息:1>.事件发生次数的统计(如:user commits)2>._oracle v$sysstat视图

随便推点

Vue router报错:NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated"}的解决方法_navigationduplicated {_name: 'navigationduplicated-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏9次。我最近做SPA项目开发动态树的时候一直遇到以下错误:当我点击文章管理需要跳转路径时一直报NavigationDuplicated {_name: “NavigationDuplicated”, name: “NavigationDuplicated”}这个错误但是当我点击文章管理后,路径跳转却是成功的<template> <div> 文章管理页面 <..._navigationduplicated {_name: 'navigationduplicated', name: 'navigationduplic

Webrtc回声消除模式(Aecm)屏蔽舒适噪音(CNG)_webrtc aecm 杂音-程序员宅基地

文章浏览阅读3.9k次。版本VoiceEngine 4.1.0舒适噪音生成(comfort noise generator,CNG)是一个在通话过程中出现短暂静音时用来为电话通信产生背景噪声的程序。#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)static const EcModes kDefaultEcMode = kEcAecm;#elsestati..._webrtc aecm 杂音

医学成像原理与图像处理一:概论_医学成像与图像处理技术知识点总结-程序员宅基地

文章浏览阅读6.3k次,点赞9次,收藏19次。医学成像原理与图像处理一:概论引言:本系列博客为医学成像原理与图像处理重要笔记,由于是手写,在此通过扫描录入以图片的形式和电子版增补内容将其进行组织和共享。前半部分内容为图像处理基础内容,包括图像的灰度级处理、空间域滤波、频率域滤波、图像增强和分割等;后半部分内容为医学影象技术,包括常规胶片X光机、CR、DR、CT、DSA等X射线摄影技术、超声成像技术、磁共振成像(MRI)技术等。本篇主要内容是概论。_医学成像与图像处理技术知识点总结

notepad++ v8.5.3 安装插件,安装失败怎么处理?下载进度为0怎么处理?_nodepa++-程序员宅基地

文章浏览阅读591次,点赞13次,收藏10次。notepad++ v8.5.3 安装插件,下载进度为0_nodepa++

hive某个字段中包括\n(和换行符冲突)_hive sql \n-程序员宅基地

文章浏览阅读2.1w次。用spark执行SQL保存到Hive中: hiveContext.sql(&quot;insert overwrite table test select * from aaa&quot;)执行完成,没报错,但是核对结果的时候,发现有几笔数据超出指定范围(实际只包含100/200)最终排查到是ret_pay_remark 字段包含换行符,解决方案:执行SQL中把特殊字符替换掉regexp_replace(..._hive sql \n

印象笔记05:如何打造更美的印象笔记超级笔记_好的印象笔记怎么做的-程序员宅基地

文章浏览阅读520次,点赞10次,收藏8次。印象笔记05:如何打造更美的印象笔记超级笔记本文介绍印象笔记的具体使用,如何打造更美更实用的笔记。首先想要笔记更加好看和实用,我认为要使用超级笔记。所谓超级笔记就是具有很多便捷功能的笔记。_好的印象笔记怎么做的

推荐文章

热门文章

相关标签