一、SAP R/3体系结构
SAP R/3一个分为三层:数据库层、应用层、表示层。其中应用层和数据库层由一个系统构成。
表示层:表示层(Presentation Layer)简单来讲其实就是指个人PC,是保存构成SAPGUI(GraphicalUserInterface)的软件组件(Software Component)的机器。在这个层次提供 R/3 系统与用户之间的接口。SAPGUI是指安装在个人PC上的终端设备,用户就通过此设备输入或者查询数据。
应用层:SAP所有的程序都在应用层执行,SAP GUI只是起到终端的作用,运营服务器的应用层由一个或多个应用服务器与一个文档服务器(Message Server)组成。通过文档服务器与应用服务器相互连接,使用户自动选择符合小的服务器登录。其中可以使用多个应用服务器分散系统的负荷。SAP 中的应用层上存在本地缓冲器,若在 ABAP 数据字典中的数据库表上设置缓冲器,则不访问数据库层而直接在本地缓冲器中读取数据。这种设置可以提高SAP 性能,但只适用于 OPEN SOL。分配器(Dispatcher) 用于适当地分配工作流( Work Process
数据库层:数据库层(Database Layer) 与本章要学习的 SQL 语密切关系,SAP 中的所有数据都存储在这一层即数据库存储主数据,事务码以及 R/3 系统的所有数据。在应用层上创建用户与画面相互作用(Dialog)的 GU,它主要负责查询/维护数据库层的数据。在数据库层通过 DBMS 可以创建和管理如数据库表/视图/结构体等数据库对象。在这种环境下,开发者通过应用程序的 GU画面修改数据或者直接连接到数据库执行 SOL。
SAP 禁止一般用户直接连接数据库修改数据,甚至有时还限制查询数据的权限。这种设置是为了保障数据的完整性、一贯性、安全性及透明性。当然,如果公开数据库的系统信息、ID 及密码就无法完全切断直接访问数据库的渠道。如图所示,ABAP 开发者通过ABAP数据字典管理数据库结构。
ABAP 程序中使用的对象 (数据表、视图、结构体、类型等) 称为 ABAP 数据字典这些对象的信息叫 Metadata、Data Definition 或者 Schema,另外,通过这些ABAP 数据字典来定义及管理数据结构。
二、OPEN SQL
读取数据:
INTO:当查询一条数据时使用结构体、变量、工作区;当查询多条数据时使用内表,往内表插入数据是要使用append。
Package size:一次性读取n条数据。需要使用endselect语句。
From:
Where:
例1(常规查询):
DATA:gt_itab TYPE STANDARD TABLE OF sflight,
gs_wa TYPE sflight.
*查询出来的结果放入结构体
SELECT * INTO gs_wa FROM sflight WHERE carrid = 'AA'.
WRITE:/ gs_wa-carrid,gs_wa-connid.
ENDSELECT.
WRITE / '--------------------------------这里是分界线-----------------------------------'.
*查询结果用内表保存
SELECT * INTO TABLE gt_itab FROM sflight WHERE carrid = 'AA'.
LOOP AT gt_itab INTO gs_wa. "利用loop循环遍历输出
WRITE:
/ gs_wa-carrid,gs_wa-connid.
ENDLOOP.
例2(动态sql-查询字段):
*动态SQL语句
DATA:gs_line(72) TYPE c,
gt_list LIKE TABLE OF gs_line(72).
gs_line = 'CARRID CONNID'.
SELECT DISTINCT (gs_line) INTO TABLE gt_itab
FROM sflight WHERE CARRID = 'AA'.
IF sy-subrc = 0.
LOOP AT gt_itab INTO gs_wa.
WRITE: / gs_wa-carrid,gs_wa-connid.
ENDLOOP.
ENDIF.
例3(package):
*package一次性取出n条数据循环输出
DATA NUM TYPE I VALUE 1.
SELECT carrid connid
FROM spfli
INTO CORRESPONDING FIELDS OF TABLE gt_itab
PACKAGE SIZE 5.
WRITE: / '第',NUM,'次取出的数据'.
NUM = NUM + 1.
LOOP AT gt_itab INTO gs_wa.
WRITE: / gs_wa-carrid,gs_wa-connid.
ENDLOOP.
ENDSELECT.
例4(动态使用数据库表名称):
*动态使用数据库名称
PARAMETERS p_tname TYPE char10.
DATA gs_wa TYPE sflight.
SELECT SINGLE * INTO gs_wa FROM (p_tname)
WHERE carrid = 'AA'.
WRITE:/ gs_wa-carrid,gs_wa-connid.
例5(INNER JOIN使用方法):
*INNER JOIN使用方法
TYPES: BEGIN OF t_str,
carrid TYPE sflight-carrid,
carrname TYPE scarr-carrname,
END OF t_str.
DATA:gs_str TYPE t_str.
SELECT SINGLE a~carrid b~carrname
INTO CORRESPONDING FIELDS OF gs_str
FROM sflight AS a
INNER JOIN scarr AS b
ON a~carrid = b~carrid
WHERE a~carrid = 'AA'.
WRITE : gs_str-carrid,gs_str-carrname.
例6(outer join使用方法):
*outer join使用方法
TYPES : BEGIN OF t_str,
carrid TYPE sflight-carrid,
carrname TYPE scarr-carrname,
END OF t_str.
DATA:gs_str TYPE t_str.
SELECT SINGLE a~carrid b~carrname
INTO CORRESPONDING FIELDS OF gs_str
FROM sflight AS a
LEFT JOIN scarr AS b
ON a~carriD = b~carrid
WHERE a~carrid = 'AA'.
WRITE: gs_str-carrid,gs_str-carrname.
例7(动态设置筛选条件):
*动态设置查询条件
DATA gs_where TYPE c LENGTH 72.
DATA gv_carrname TYPE scarr-carrname.
PARAMETERS carrid TYPE scarr-carrid.
CONCATENATE 'CARRID = ''' carrid '''' INTO gs_where. "拼接字符串:CARRID = 'AC'.
SELECT SINGLE carrname
INTO gv_carrname
FROM scarr
WHERE (gs_where).
WRITE / gv_carrname.
例8(动态设置多个筛选条件):
*两个动态参数
TYPES : BEGIN OF gs_carr, "自定义结构体
carrid TYPE scarr-carrid,
carrname TYPE scarr-carrname,
END OF gs_carr.
DATA gs_where TYPE c LENGTH 72.
DATA gt_where LIKE TABLE OF gs_where.
DATA gT_carr TYPE gs_carr. "实例化结构体
PARAMETERS carrid1 TYPE scarr-carrid.
PARAMETERS carrid2 TYPE scarr-carrid.
CONCATENATE 'CARRID = ''' carrid1 '''' INTO gs_where.
APPEND gs_where TO gt_where.
gs_where = 'OR'.
APPEND gs_where TO gt_where.
CONCATENATE 'CARRID = ''' carrid2 '''' INTO gs_where.
APPEND gs_where TO gt_where. "字符串拼接
SELECT *
INTO CORRESPONDING FIELDS OF gT_carr "结果输出到结构体中(自动找到重名的字段进行赋值)
FROM scarr
WHERE (gt_where).
WRITE: / gT_carr-carrid,gt_carr-carrname.
ENDSELECT.
例(在gui上执行sql语句):
PARAMETERS :p_sql(255) TYPE c.
DATA: go_sql TYPE REF TO cl_sql_statement. "cl_sql_statement是一个类
DATA: gv_sql TYPE string.
DATA: gd_ret TYPE i.
CREATE OBJECT go_sql.
gv_sql = p_sql.
TRANSLATE gv_sql TO UPPER CASE.
CALL METHOD go_sql->execute_update "execute_update是cl_sql_statement类的一个方法
EXPORTING
statement = gv_sql "输入SQL语句
RECEIVING
rows_processed = gd_ret. "接收数据
WRITE: / '影响条数', gd_ret.
FOR ALL ENTRIES:FOR ALL ENTRY 语句与套 SELECT 语或 Subquery 的能相似。使用 FOR ALLENTRY 语句时,WHERE 语句中使用的条件必须是 itab 中存在的字段。
例:
*FOR ALL ENTRIES语句使用
DATA gt_spfli TYPE TABLE OF spfli.
DATA gt_sflight TYPE TABLE OF sflight.
DATA gs_sflight TYPE sflight.
SELECT * FROM spfli INTO TABLE gt_spfli. "供下面做查询表用
SELECT * FROM sflight INTO TABLE gt_sflight
FOR ALL ENTRIES IN gt_spfli "从上面查询出来的数据作为表继续查询
WHERE carrid = gt_spfli-carrid
AND connid = gt_spfli-connid.
LOOP AT gt_sflight INTO gs_sflight.
WRITE: / gs_sflight-carrid,gs_sflight-connid.
ENDLOOP.
Group by:分组函数(having)
例:
*GROUP BY 语句使用
DATA:gv_carrid TYPE sflight-carrid,
gv_connid TYPE sflight-connid,
gv_paymentsum TYPE i.
SELECT carrid connid AVG( paymentsum )
INTO (gv_carrid,gv_connid,gv_paymentsum)
FROM sflight GROUP BY carrid connid.
WRITE: / gv_carrid,gv_connid,gv_paymentsum.
ENDSELECT.
Order by:排序。用 ASCENDING升序,DESCENDING 降序
- 根据表的 KEY 值进行排序。
- 只适用于SELECT*语句。
- 在JOIN 语和视图中无法使用。
例:
*HAVING \ ORDER BY语句使用
SELECT carrid connid AVG( paymentsum ) AS pay
INTO (gv_carrid,gv_connid,gv_paymentsum)
FROM sflight GROUP BY carrid connid
HAVING AVG( paymentsum ) > 40000
ORDER BY pay.
WRITE: / gv_carrid,gv_connid,gv_paymentsum.
ENDSELECT.
Subquery:利用 Subquery 可以在 WHERE 语句中追加特殊条件。Subquery 的 SELECT 语中只能指定一个字段。
例:
*SUBQUERY 用法
DATA:gv_carrid TYPE sflight-carrid,
gv_connid TYPE sflight-connid,
gv_paymentsum TYPE sflight-paymentsum.
SELECT SINGLE CARRID CONNID PAYMENTSUM
INTO (gv_carrid,gv_connid,gv_paymentsum)
FROM SFLIGHT AS A
WHERE CARRID IN
( SELECT CARRID FROM SPFLI
WHERE CARRID = A~CARRID AND CONNID = A~CONNID
)
AND A~CARRID = 'AA'.
WRITE: / gv_carrid,gv_connid,gv_paymentsum.
修改数据:
Insert:
插入一条数据:
插入多条数据:往数据库表一次性插入内表的所有值。插入相同主键的数据时会发生 dump error,为了避免发生这种错误要使用ACCEPTING DUPLICATE KEYS 语。INSERT 语执行失败系统变量SY-SUBRC会返回4。
例:
*插入多条数据
DATA : gt_spfli TYPE TABLE OF spfli,
gs_spfli TYPE spfli.
gs_spfli-carrid = 'CN'.
GS_spfli-connid = '001'.
gs_spfli-cityfrom = 'Beijing'.
gs_spfli-cityto = 'Seoul'.
APPEND gs_spfli TO gt_spfli.
gs_spfli-carrid = 'CN'.
GS_spfli-connid = '002'.
gs_spfli-cityfrom = 'Beijing'.
gs_spfli-cityto = 'Busan'.
APPEND gs_spfli TO gt_spfli.
INSERT spfli FROM TABLE gt_spfli ACCEPTING DUPLICATE KEYS.
Update:
修改一条数据:
修改多条数据:
例:
DATA gs_spfli TYPE spfli.
MOVE 'CN' TO gs_spfli-carrid.
MOVE '011' TO gs_spfli-connid.
MOVE 'shanghai' TO gs_spfli-cityfrom.
*一条数据
update spfli from gs_spfli.
UPDATE spfli SET cityto = 'beijing'
*多条数据
WHERE carrid = 'CN' AND connid = '001'.
IF sy-subrc = 0.
WRITE 'update is success!!!'.
ENDIF.
Delete:
删除一条数据:
删除多条数据:
例:
DATA gs_spfli TYPE spfli.
gs_spfli = 'CN'.
DELETE spfli FROM gs_spfli.
DELETE FROM spfli WHERE carrid = 'CN'.
IF sy-subrc = 0.
WRITE 'delete success.'.
ENDIF.
Modify:
MODIFY 既有 UPDATE 功能也有INSERT功能。当数据库表中存在该值时会以主键为基准更新此数据,若不存在则会插入此数据。可以插入/更新一条或多条数据,<target>用于指定数据库表名字使用时还可以动态地定义。
一条数据:
多条数据:
例:
DATA:gt_spfli TYPE TABLE OF spfli,
gs_spfli TYPE spfli.
*一条数据
gs_spfli-carrid = 'CN'.
gs_spfli-connid = '001'.
gs_spfli-cityfrom = 'BEIJING'.
MODIFY spfli FROM gs_spfli.
*多条数据
gs_spfli-carrid = 'CN'.
gs_spfli-connid = '001'.
gs_spfli-cityfrom = 'shanghai'.
APPEND gs_spfli TO gt_spfli.
gs_spfli-carrid = 'CN'.
gs_spfli-connid = '003'.
gs_spfli-cityfrom = 'shanghai'.
APPEND gs_spfli TO gt_spfli.
MODIFY spfli FROM TABLE gt_spfli.
三、 NATIVE SQL
简介:OPEN SQL 不管使用任何数据库类型(Oracle、Informix、DB2 等),其语法都相同,而NATIVE SOL 随着数据库类型的不同其相应的 SQL 语也有所差异。NATIVE SOL 是适用于 Oracle 数据库的,因此若设置了 MS SOL等其他数据库的话就不能正常执行。在ABAP 程序中 NATIVE SOL 虽然没有 OPEN SOL 使用频繁,但是可以实现使用OPEN SQL 无法实现的复杂的 SQL 语句。
优点:可以访问 ABAP 数据库中没有的数据库表(Oracle 系统数据表等)。是依赖于数据库的SOL 语句,因此可以实现在 OPEN SOL中无法实现的 SOI
缺点:无法鉴定(Syntax Check)EXEC ~ ENDEXEC 之间的语法,因此常会发生 Dump Error。在使用不同数据库的系统中无法使用含有 NATIVE SQL 的ABAP 程序。在 SQL 语中一定要追加用于区分客户的 mandt 字段。
语法:
NATIVE SQL 随着使用不同的数据库类型其 SQL 语也有所差异。基本语句以 EXECSQL.开头、以ENDEXEC.结束。使用变量时要在变量名前追加冒号“.”,另外,NATIVE SOI中一定要追加区分客户的MANDT 字段。
简单查询:
*简单查询
DATA: gv_carrid LIKE sflight-carrid VALUE 'AA',
gv_connid LIKE sflight-connid.
"into要空格后再接冒号
EXEC SQL.
select a.connid
into :gv_connid
from sflight a
where a.mandt = :sy-mandt and a.carrid = :gv_carrid
ENDEXEC.
WRITE gv_connid.
performing 用法:
TYPES: BEGIN OF t_str,
carrid LIKE sflight-carrid,
connid LIKE sflight-connid,
END OF t_str.
DATA gt_itab TYPE TABLE OF t_str.
DATA gs_wa TYPE t_str.
DATA gv_carrid LIKE sflight-carrid VALUE 'AA'.
EXEC SQL PERFORMING append_itab.
select a.carrid,a.connid
into :gs_wa-carrid,:gs_wa-connid
from sflight a
where a.mandt = :sy-mandt
and a.carrid = :gv_carrid
ENDEXEC.
FORM append_itab.
WRITE: / gs_wa-carrid,gs_wa-connid.
APPEND gs_wa TO gt_itab.
CLEAR gt_itab.
ENDFORM.
多表连接:
TYPES: BEGIN OF t_str,
carrid LIKE sflight-carrid,
connid LIKE sflight-connid,
carrname LIKE scarr-carrname,
END OF t_str.
DATA gs_wa TYPE t_str.
DATA: gv_carrid LIKE sflight-carrid VALUE 'AA'.
EXEC SQL PERFORMING write_data.
select a.carrid,b.carrname
into :gs_wa-carrid,:gs_wa-connid
from sflight a,scarr b
where a.mandt = b.mandt
and a.carrid = b.carrid
and a.mandt = :sy-mandt
and a.carrid = :gv_carrid
ENDEXEC.
FORM write_data.
WRITE: / gs_wa-carrid,gs_wa-carrname.
ENDFORM.