相关文章推荐
慷慨的圣诞树  ·  HiveServer2 ...·  3 周前    · 
开朗的茄子  ·  Azure Database for ...·  2 周前    · 
胆小的排球  ·  为 DBA 提供的最重要特性·  1 周前    · 
勤奋的肉夹馍  ·  SQL 求 3 列异值的 4 ...·  1 周前    · 
严肃的皮蛋  ·  sql like 多个值 - CSDN文库·  1 周前    · 
欢快的南瓜  ·  子查詢 (SQL Server) - ...·  9 月前    · 
孤独的咖啡  ·  C#调用Delphi ...·  1 年前    · 

追加读取 APPENDING .. 265

CORRESPONDING  FIELDS  OF  [WA/TABLE]… .. 266

[PACKAGE SIZE <n>] ... 267

SQL 查询条件 ... 267

operator 比较操作符 ... 267

BETWEEN .. 268

LIKE . 268

IN .. 268

NOT . 269

NULL . 269

使用 RANG 条件内表进行查询 ... 270

动态指定查询列 ... 270

动态指定表 ... 271

动态指定查询条件 ... 272

指定集团 CLIENT SPECIFIED ... 273

禁止使用缓冲 BYPASSING BUFFER ... 273

限定选择行数 UP TO n ROWS ... 273

SELECT 语句嵌套 ... 274

FOR ALL ENTRIES 选项 ... 274

联合查询 ... 277

子查询 ... 279

ALL ANY SOME 子查询 ... 279

[NOT] IN 子查询 ... 280

[NOT] EXISTS 子查询 ... 280

统计函数 ... 281

分组 ... 281

分组过滤 ... 282

排序 ... 282

通过游标读取数据 ... 283

更新数据 ... 284

INSERT . 284

UPDATE . 285

DELETE . 286

MODIFY (插入或修改) ... 288

其他技术 ... 288

使用表工作区 Interface work areas . 288

操作时间分析(类似 System.currentTimeMillis() 方法) ... 289

数据库优化 ... 289

使用 Buffer Tables

只有标准 SQL DML 有对应的 Open SQL ,只有 SELECT INSERT, UPDATE, DELETE 有对应的 Open SQL

Native SQL 不会用到缓存,会直接发送给数据库,而不会经过 ABAP 数据访问层。除开 DML ,它一般用于 DDL DCL ,主要用来维护数据库表

ABAP 中的 Database Interface 层负责将 Open SQL 转换成相应的数据库所支持的 Standard SQL

Open SQL

ABAP Dictionary 属于 ABAP Workbench 的一部分 可以用它来创建与管理数据库表

O pen SQL 只能访问那些通过 ABAP Dictionary 创建的表(通过 ABAP Dictionary 创建的表才会显示在 Dictionary 里),而不能操作那些直接通过数据库工具创建的表。 ABAP Dictionary 通过标准的 DDL SQL 创建与维护数据库表。

Open SQL contains the following keywords:

读取数据 SELECT

SELECT { SINGLE [FOR UPDATE] }|{[ DISTINCT ]{}}

* | { { col1|{ MAX ( [ DISTINCT ] col )
|
MIN ( [ DISTINCT ] col )

|
AVG ( [ DISTINCT ] col )
|
SUM ( [ DISTINCT ] col )
|
COUNT ( DISTINCT col )
|
COUNT
( * ) } } [ AS a1 ] ... } | ( column_syntax )

FROM { {dbtab [ AS tabalias]}

| [ ( ] { {dbtab_left [ AS tabalias_left]} | join_expression_ 即两个或两个以上的表前部分 JOIN 表达式 }
{
[ INNER ] JOIN }|{ LEFT [ OUTER ] JOIN }
{dbtab_right [
AS tabalias_right] ON join_cond} [ ) ]

|{ ( dbtab_syntax ) [ AS tabalias]} }
[
CLIENT SPECIFIED
]
[
UP TO n ROWS
]
[
BYPASSING BUFFER
]

{ INTO {{[CORRESPONDING FIELDS OF] wa}|(dobj1, dobj2, ...) } }

| { INTO | APPENDING [CORRESPONDING FIELDS OF] TABLE itab [ PACKAGE SIZE n] }

[[ FOR ALL ENTRIES IN itab ] WHERE [ ... col {=|EQ|<>|NE|>|GT|<|LT|>=|GE|<=|LE} itab - comp ... ]

{ { col1 { =|EQ|<>|NE|>|GT|<|LT|>=|GE|<=|LE }

{ {dobj} | { col2 } | { [ALL|ANY|SOME] (select … from …) } } }
| {col [
NOT ] BETWEEN dobj1 AND
dobj2}
| {col [
NOT ] LIKE dobj [ ESCAPE
esc]}
| {col [
NOT ] IN ( dobj1 , dobj2 ...
)}
| {col [
NOT ] IN
seltab}
| {col
IS [NOT] NULL
}
| {
( cond_syntax )
}
| {
[NOT] EXISTS (select … from …)
}
| {col
[NOT] IN (select … from …)
} }]

[ GROUP BY { {col1 col2 ... } | ( column_syntax ) }][ HAVING sql_cond]
[
ORDER BY { { PRIMARY KEY
}
| { col1 [
ASCENDING | DESCENDING
]
col2 [
ASCENDING | DESCENDING ] ...
}
|
( column_syntax ) }] .

...
[
ENDSELECT ] .

如果从数据库读出来的数据存在重复时,是不能存储到 Unique 内表中去的——如 Unique 的排序表与哈希表

SELECT SINGLE [ FOR UPDATE ] <cols> ... INTO [ CORRESPONDING FIELDS OF ] wa WHERE ...
SELECT SINGLE [ FOR UPDATE ] <cols> ... INTO ( dobj1 , dobj2 , ... ) WHERE ...

SINGLE 选项不能用在 子查询

如果有多条,则返回第一条数据

不能使用 APPENDING ,并且 INTO 后面不能接内表

为了确保只读到一条数据,必须在 WHERE 语句后面指定所有的 primary key ,否则语法检查时会警告

FOR UPDATE 只能与 SINGLE 一起使用 即只能对一条数据进行加 exclusive lock 。如果产生死锁 会抛异常

FOR UPDATE 会绕过数据库缓存

DATA wa TYPE spfli.
SELECT SINGLE
*
FROM
spfli
INTO CORRESPONDING FIELDS OF
wa
WHERE carrid = 'LH' AND connid = '0400'
.

注:一般使用 SINGLE 是表示根据表的关键字来查询,这样才能确保只有一条数据,所以当 使用 SINGLE 时,不能再使用 ORDER BY 语句 (因为没有必要了),如果查询时不能根据关键字来查询,但要求在查询时先排序再取第一条时,我们只能使用另一种语法(其实更好的方式是请参考 UP TO n ROWS ):

SELECT ... UP TO 1 ROWS ... ORDER BY

ENDSELECT.

DISTINCT

If DISTINCT is used, the SELECT statement bypasses SAP buffering

两种选择 选择至内表与循环选择

选择至内表

SELECT [DISTINCT] <cols> ... WHERE ...

循环选择 的语法

SELECT [DISTINCT] <cols> ...INTO CORRESPONDING FIELDS OF <wa> WHERE ...

ENDSELECT .

循环发送 SQL 语句 每次读取的结果 INTO 到一个结构对象里 而不是内表里。 .

如果至少读取到一条数据,在读取后(以及循环读取后) SY-SUBRC 0 ,否则设置为 4 。读取到的行会存储在 SY-DBCNT 里,如果在 SELECT…ENDSELECT 循环里,每执行一次 SELECT 语句, SY-DBCNT 都加 1

从技术角度 SELECT loops 里可以嵌套 SELECT loops ,但 出于性能考虑,一般使用 a join in the FROM clause or a subquery in the WHERE clause

DATA wa TYPE spfli.
SELECT * INTO CORRESPONDING FIELDS OF wa FROM spfli WHERE carrid EQ 'LH'
.
WRITE
: / sy-dbcnt,wa-carrid, wa-connid, wa-cityfrom, wa-cityto.
ENDSELECT
.

SELECT col1 [AS <a1>] col2 [AS <a2>] ...

col 可以是数据库表里的字段名,但如果从多个表里联合查询时,会有重名的字段,此时只能使用别名 [AS <a1>] 选项了

col 可以是数据库表里的字段名 dbtab ~ col1 dbtab ~ col2 ,这种主要用在多个表查询同名字段时使用,否则编译出错

col 还可以是 tabalias ~ col 形式, tabalias 为数据库表的别名,需在 FROM 从句定义

注:使用别名后,别名将会替代 INTO ORDERBY 从句中原字段名使用

存储到指定变量中

SELECT ... INTO (<f 1 >, <f 2 >, ...). ...

此情况下要求 SELECT 从句后面的选择字段个数与 INTO 从句后面的字段列表个数相同(与名称无关,只与类型相关)

选择多个字段时 我们也可以将这些字段一个个插入到简单变量中 而不存储到一个结构体中 并且如果要存储到多个变量中 则需要将这些对象放在括号中
DATA : wa_carrid TYPE spfli-carrid,
wa_connid
TYPE
spfli-connid.
SELECT SINGLE carrid connid FROM
spfli
INTO (wa_carrid , wa_connid )

WHERE cityfrom = 'NEW YORK' AND cityto = 'SAN FRANCISCO'
.
WRITE
: wa_carrid,wa_connid.

注意使用种形式中, INTO 后面的左括号与变量名之间不能有空格,其他可以有。

DATA : average TYPE p DECIMALS 2 ,
sum
TYPE p DECIMALS 2 .
SELECT AVG ( luggweight ) SUM ( luggweight ) INTO ( average , sum ) FROM sbook .

也可这样:

DATA : BEGIN OF luggage ,
average
TYPE p DECIMALS 2 ,
sum
TYPE p DECIMALS 2 ,
END OF luggage .
SELECT AVG ( luggweight ) AS average SUM ( luggweight ) AS sum INTO CORRESPONDING FIELDS OF luggage FROM sbook .

SELECT * INTO…

DATA wa TYPE spfli . " 定义成与数据库表结构相同的结构
SELECT * INTO wa FROM spfli .
WRITE : / wa - carrid ...
ENDSELECT .

注:下面两种省略 INTO 时, SELECT 从句后面只能是星号 * ,否则编译不通过

DATA spfli TYPE spfli .
SELECT * FROM spfli . " 如果表名与存储区结构同名 还可以省略
WRITE : / spfli - carrid ...
ENDSELECT .

TABLES spfli . " 也可直接使用 TABLES 语句定义与数据库表同名的结构
SELECT * FROM spfli .
WRITE : / spfli - carrid ...
ENDSELECT .

追加读取 APPENDING

SELECT ... INTO| APPENDING [CORRESPONDING FIELDS OF] TABLE <itab>…

CORRESPONDING  FIELDS  OF  [WA/TABLE]…

SELECT ... INTO [ CORRESPONDING FIELDS OF ] <wa> ...

该语句读取一条数据到结构 <wa> 中。只能使用 SINGLE 选项或与 ENDSELECT 一起使用

如果没有 [CORRESPONDING FIELDS OF] 选项, <wa> 结构至少要大小或等于 SELECT 语句选择所有字段长度,但此时从数据库中查询出来的数据存储到结构 <wa> 时,与结构中的名称无关,它会将查询出来的字段从左到右存储到到 <wa> 结构中, WA 尾部以前的内容可能不会被修改

如果加上了 [CORRESPONDING FIELDS OF] 选项选择,则会将从数据库查询出来的字段值存储到 <wa> 结构中同名字段中,此种情况是根据 SELECT 从句选择字段名与结构 <wa> 中组件字段名来映射的 SELECT 从句所选择字段个数可以大于或小于、等于 <wa> 结构中的字段个数。

如果查询出来的结果只有一列,则 WA 可以直接是基本类型的无列名的结构

SELECT ... INTO (dobj1, dobj2, ...)

SELECT 后面列按顺序依次存入 dobj1, dobj2, ...

DATA wa TYPE spfli.
SELECT
carrid connid cityfrom cityto
FROM
spfli
INTO
(wa-carrid, wa-connid, wa-cityfrom, wa-cityto).
WRITE
: / wa-carrid, wa-connid, wa-cityfrom, wa-cityto.
ENDSELECT
.

SELECT ... INTO | APPENDING [CORRESPONDING FIELDS OF] TABLE <itab>...

从左到右存储,或者根据名称映射存储:

DATA : BEGIN OF strc OCCURS 10 ,
mandt
LIKE mara - mandt ,
matnr1
LIKE mara - matnr ,
END OF strc .
" 运行时出错 因为 Select 了三个字段 strc 只有两个
"select SINGLE ersda matnr mandt FROM mara INTO  strc .
" 即使 Select 了三个字段, strc 只有两个这里不会报错。结果只将 mandt 储存到 strc 中,因为使用了 CORRESPONDING 选项
select SINGLE ersda matnr mandt FROM mara INTO CORRESPONDING FIELDS OF strc .
CLEAR strc .
" 结果是 matnr 存储到 strc-mandt mandt 存储到 strc-matnr1 中,规则是 Select 的顺序与 strc 中字段声明顺序对应起来(从左到右,与名称无关)
select SINGLE matnr mandt FROM mara INTO strc .

使用 CORRESPONDING FIELDS 选项,只是将同名字段进行存储:

DATA wa_spfli TYPE spfli.
SELECT SINGLE
carrid connid
FROM
spfli
INTO
CORRESPONDING FIELDS OF wa_spfli
WHERE cityfrom = 'NEW YORK' AND cityto = 'SAN FRANCISCO'
.
WRITE
:wa_spfli-carrid,wa_spfli-connid.

将数据一次性存储到内表中:

DATA : itab TYPE STANDARD TABLE OF spfli,wa LIKE LINE OF itab.
SELECT
carrid connid cityfrom cityto
INTO CORRESPONDING FIELDS OF
TABLE itab
FROM
spfli
WHERE carrid EQ 'LH'
.

]"> [PACKAGE SIZE <n>] ...

只能用在 SELECT ... ENDSELECT 语句间。其作用是防止一次性将大量数据写入内表时,发生内存溢出问题,所以可以分批来读取,此种情况下不适使用 APPENDING 选项

如果使用 FOR ALL ENTRIES 选项,则可能还是会出现从数据库中读取大量数据时出现内存溢出的情况,因为 FOR ALL ENTRIES 的过程是这样的:先将所有数据从数据库中读取并临时存储到了个系统内表中,而 PACKAGE SIZE 只用于将数据从系统内表中分批读取出来存储到目标内表中,所以使用 FOR ALL ENTRIES 还是可能会出现内存溢出的问题

DATA : wa TYPE spfli ,
itab
TYPE SORTED TABLE OF spfli WITH UNIQUE KEY carrid connid .

SELECT carrid connid
FROM
spfli
INTO CORRESPONDING FIELDS OF TABLE
itab
PACKAGE SIZE 3 .

LOOP AT itab INTO wa .
WRITE : / wa - carrid , wa - connid .
ENDLOOP .
SKIP 1 .
ENDSELECT .

SQL 查询条件

operator 比较操作符

SELECT ... WHERE <s><operator><f> ...

<f> 可能是 FROM 从句中的某个 表的表字段 ,也可以是 数据变量 常量 值,或者是具有返回值的 子查询 <operator> 比较操作符 可以是以下这些:

BETWEEN

... col [ NOT ] BETWEEN dobj1 AND dobj2 ...

SELECT carrid connid fldate
FROM
sflight
INTO CORRESPONDING FIELDS OF TABLE
sflight_tab
WHERE
fldate BETWEEN sy-datum AND date.

SELECT ... WHERE <s> [ NOT ] LIKE <f> [ESCAPE <h>] ...

_ ”用于替代单个字符,“ % ”用于替代任意字符串,包括空字符串。

可以使用 ESCAPE 选项指定一个忽略符号 h ,如果通配符“ _ ”、“ %” 前面有符号 <h> ,那么通配符失去了它在模式中的功能,而指字符本身了:

... WHERE FUNCNAME LIKE 'EDIT#_%' ESCAPE '#'.

以“ EDIT_ ”开头的字符串

SELECT ... WHERE <s> [ NOT ] IN (<f 1 >, ......, <f n >) ...

... WHERE CITY IN ('BERLIN', 'NEW YORK', 'LONDON').

如果 CITY IN 后面列表中的任何一个时返回 true

SELECT ... WHERE <s> [ NOT ] IN <seltab> ...

<seltab> 为选择屏幕中的条件选择内表

如果选择内表内容如下 ( 顺序不重要 ):

SIGN  OPTION  LOW              HIGH

----------------------------------------------------------------------

I     EQ      01104711

I     BT      10000000         19999999

I     GE      90000000

E     EQ      10000911

E     BT      10000810         10000815

E     CP      1%2##3#+4++5*

则会生成如下的 WHERE 语句 :

... ( ID = '01104711' OR ID BETWEEN '10000000' AND '19999999' OR ID >= '90000000' )
AND ID <> '10000911'
AND ID NOT BETWEEN '10000810' AND '10000815'
AND ID NOT LIKE '1#%2##3+4__5%' ESCAPE '#'...

seltab 条件内表生成 WHERE 条件语句的详细规则可以参考《 User Dialogs.docx 》中的“ SELECT-OPTIONS ---- 选择内表多条件组合规则 ”章节。

DATA spfli_wa TYPE spfli.
SELECT - OPTIONS : s_carrid
FOR spfli_wa-carrid ,
s_connid
FOR
spfli_wa-connid .
SELECT SINGLE
*
FROM
spfli
INTO
spfli_wa
WHERE
carrid IN s_carrid AND
connid
IN s_connid.

SELECT ... WHERE <s> [ NOT ] IN < subquery > ...

子查询返回的某列的多个值

SELECT ... WHERE NOT <cond> ...

... WHERE ( NUMBER = '0001' OR NUMBER = '0002' ) ANDNOT ( COUNTRY = 'F' OR COUNTRY = 'USA' ).

SELECT ... WHERE <s> IS [ NOT ] NULL ...

在数据库中,有一种字段值为空值( NULL ),表示该数据没有任何数据内容,然而在 ABAP 中,特定的数据对象都具有初始值,这些初始值可能是一串 0 或空格等,但不等同与数据库中的 NULL 值,因而, 在使用 ABAP 语句向数据库表中插入数据时,所有的数据字段都不可能是 NULL 。但是,在 ABAP 数据词典里仍有可能出现空值字段,因为在程序中可以使用 Native SQL 进行空值设定,用数据词典工具添加数据条目时也可能出现 NULL ,但在数据查询过程中,如果将 NULL 字段的值读入到程序变量中,会转化为 ABAP 中相应字段的初始值, NULL 值会使用以下值替换( 更多关于 ABAP 数据库表的“空”、“ NULL ”与“ Initial Values ”请参考《 ABAP Work Note.docx 》中的“ 表字段中的 Initial Values ”与“ 关于 ABAP 中空的概念 ”章节):

DATENTYP         INITIALWERT
_______________________________________________
ACCP             ' ' blank
CHAR            ' ' blank
CLNT             000
CUKY            ' ' blank
CURR              0
DATS             00000000
DEC             0
FLTP              0
INT1              0
INT2              0
INT4             0
LANG            ' ' blank
NUMC            0000...  for field lenth <= 32
No initial value for field length > 32
QUAN             0
RAW             No initial value provided
TIMS             000000
UNIT            ' ' blank
VARC            No initial value,
since VARC not supported from 3.0 onwards
LRAW            No initial value provided
LCHR            No initial value provided

使用 RANG 条件内表进行查询

条件内表过时定义法

RANGES seltab FOR dobj [ OCCURS n].

其中 dobj 为已定义的某个变量

新的语法

DATA : BEGIN OF seltab OCCURS 0 ,
sign
TYPE c LENGTH 1
,
option
TYPE c LENGTH 2
,
low
LIKE
dobj,
high
LIKE
dobj,
END OF
rtab.

另外 选择屏幕上 SELECT-OPTIONS 语句定义的参数就是一个 RANG 条件内表 可以直接使用在下面 WHERE 从句中。

通过以上定义 RANG 条件内表后可以在 WHERE 语句中如下直接使用:

WHERE … field [NOT] IN seltab …

如果 RANG 条件内表为空,则 INseltab 逻辑表达试恒为真

动态指定查询列

SELECT … ( column_syntax ) ...

column_syntax 可以是 character-like data object ,或者是标准内表,不区分大小写

如果 column_syntax initial 则相当于 * 会查询所有列

如果 column_syntax 是一个带有表的内表,则语句中的 column_syntax 还是代表的为表体而不是表头

DATA : itab TYPE STANDARD TABLE OF spfli WITH HEADER LINE .
DATA : line ( 72 ) TYPE c
,
list
LIKE TABLE OF line
.
line = 'CARRID'
.
APPEND line TO
list.
line = 'CITYFROM CITYTO'
.
APPEND line TO
list.
"
以内表作为动态列
SELECT DISTINCT (list) INTO CORRESPONDING FIELDS OF TABLE itab FROM spfli UP TO 2 ROWS .
LOOP AT
itab.
WRITE
: / itab-cityfrom, itab-cityto,itab-carrid.
ENDLOOP
.
CLEAR
: itab[],itab.
"
以字符串作为动态列
SELECT DISTINCT ( line ) INTO CORRESPONDING FIELDS OF TABLE itab FROM spfli UP TO 2 ROWS .
SKIP
.
LOOP AT
itab.
WRITE
: / itab-cityfrom, itab-cityto,itab-carrid.
ENDLOOP
.

NEW YORK             SAN FRANCISCO        DL

FRANKFURT            BERLIN               LH

BERLIN               FRANKFURT

FRANKFURT            BERLIN

动态查询表中指定的某列:

" 表中的某列的列名
PARAMETERS comp LENGTH 20 .

DATA : dref TYPE REF TO data
,
long_name
TYPE
string,
ftab
TYPE TABLE OF
string.
FIELD
-SYMBOLS <fs>.

long_name =
'spfli-'
&& comp.
"
根据词典中的类型动态创建数据对象
CREATE DATA dref TYPE (long_name).
" 动态创建出来的数据对象需解引用并分配给字段符号才能使用
ASSIGN dref->* TO <fs>.
APPEND comp TO ftab. "
动态列放在内表中
SELECT DISTINCT (ftab) INTO <fs> FROM spfli WHERE carrid = 'LH' .
WRITE
: / <fs>.
ENDSELECT
.

动态指定表

SELECT ... FROM (dbtab_syntax) ...

column_syntax 可以是 character-like data object ,或者是标准内表,不区分大小写

如果 column_syntax 是一个带有表的内表,则语句中的 column_syntax 还是代表的为表体而不是表头

DATA wa TYPE scarr .
DATA name ( 10 ) VALUE 'SCARR' .
SELECT * INTO wa
FROM ( name ) CLIENT SPECIFIED WHERE mandt = '000' .
WRITE : / wa - carrid , wa - carrname .
ENDSELECT .

PARAMETERS : p_cityfr TYPE spfli-cityfrom,
p_cityto
TYPE
spfli-cityto.
DATA : BEGIN OF
wa,
fldate
TYPE
sflight-fldate,
carrname
TYPE
scarr-carrname,
connid
TYPE
spfli-connid,
END OF
wa.
DATA itab LIKE SORTED TABLE OF
wa
WITH UNIQUE KEY
fldate carrname connid.
DATA : column_syntax TYPE
string,
dbtab_syntax
TYPE
string.
column_syntax =
`c~carrname p~connid f~fldate`
.
dbtab_syntax =
`( ( scarr AS c `

&
` INNER JOIN spfli AS p ON p~carrid  = c~carrid`
&
` AND p~cityfrom = p_cityfr `
&
` AND p~cityto   = p_cityto )`
&
` INNER JOIN sflight AS f ON f~carrid = p~carrid `
&
` AND f~connid = p~connid )` .
SELECT
(column_syntax)
FROM
(dbtab_syntax)
INTO CORRESPONDING FIELDS OF TABLE
itab.

动态指定查询条件

SELECT ... WHERE (cond_syntax) ...

SELECT ... WHERE <cond>AND (cond_syntax) ...

cond_syntax 可以是 character-like data object ,或者是标准内表。 如果为 初始值 initial ,则返回结果为真

如果 cond _syntax 是一个带有表的内表,则语句中的 cond _syntax 还是代表的为表体而不是表头

动态的条件只能用在 WHERE 从句中,不能用在 ON

下面程序中,内表 itab 仅包含一个类型 C 组件且最大长度为 72 的字段,内表名称必须在括号中指定。可以将各种逻辑表达式(但除上面的 RANG 条件内表外)添加到内表行中,且在内表行中的 逻辑表达式里只能使用文字不能再使用变量

DATA : cond( 72 ) TYPE c ,
itab
LIKE TABLE OF
cond,
city1(
10 ) VALUE 'NEW YORK'
,
city2(
13 ) VALUE 'SAN FRANCISCO'
,
itab_spfli
LIKE TABLE OF spfli WITH HEADER LINE
.

CONCATENATE 'cityfrom = ''' city1 '''' INTO
cond.
APPEND cond TO
itab.
CONCATENATE 'or cityfrom = ''' city2 '''' INTO
cond.
APPEND cond TO
itab.
SELECT * INTO TABLE itab_spfli FROM spfli WHERE
(itab) .
LOOP AT
itab_spfli.
WRITE
: / itab_spfli-carrid, itab_spfli-connid.
ENDLOOP
.

另外, WHERE 后面也可以使用一个或多个拼接好的查询条件字符串:

DATA : cond1( 72 ) TYPE c ,
cond2(
72 ) TYPE c
,
city1(
10 ) VALUE 'NEW YORK'
,
city2(
13 ) VALUE 'SAN FRANCISCO'
,
itab_spfli
LIKE TABLE OF spfli WITH HEADER LINE
.
CONCATENATE 'cityfrom = ''' city1 '''' INTO
cond1.
CONCATENATE 'cityfrom = ''' city2 '''' INTO
cond2.
SELECT * INTO TABLE itab_spfli FROM spfli WHERE (cond1) OR (cond2)
.

也可以是一部分是拼接好的查询条件字符串,一部分是条件 table

DATA : cond( 72 ) TYPE c ,
cond1(
72 ) TYPE c
,
itab
LIKE TABLE OF
cond,
city1(
10 ) VALUE 'NEW YORK'
,
city2(
13 ) VALUE 'SAN FRANCISCO'
,
itab_spfli
LIKE TABLE OF spfli WITH HEADER LINE
.
CONCATENATE 'cityfrom = ''' city1 '''' INTO
cond1.
CONCATENATE 'cityfrom = ''' city2 '''' INTO
cond.
APPEND cond TO
itab.
SELECT * INTO TABLE itab_spfli FROM spfli WHERE (itab) or (cond1)
.

也可以部分是动态的条件:

DATA : cond( 72 ) TYPE c ,
itab
LIKE TABLE OF
cond,
city1(
10 ) VALUE 'NEW YORK'
,
city2(
13 ) VALUE 'SAN FRANCISCO'
,
itab_spfli
LIKE TABLE OF spfli WITH HEADER LINE
.
CONCATENATE 'cityfrom = ''' city2 '''' INTO
cond.
APPEND cond TO
itab.
SELECT * INTO TABLE itab_spfli FROM spfli WHERE (itab) or cityfrom = city1
.

指定集团 CLIENT SPECIFIED ...

一般的 SAP 数据库表在创建时,属于特定集团的表都由系统自动生成一个默认的字段 MANDT ,用于指定数据属于哪个集团。该字段也是关键字段之一。有些系统通用表是与集团无关的。

一般情况下, OPENSQL 操作时,系统会自动进行集团处理,系统只从当前集团提取数据,在程序中不能指定 MANDT 字段,否则出错。

如果确实需要在程序中指明需要操作的特定集团,则需要使用 CLIENT SPECIFIED 取消系统的自动处理功能:

SELECT | UPDATE ... <tables> CLIENT SPECIFIED ...

该选项必须跟在数据库表名称之后。关闭了系统自动集团处理后,就可以就可以在 WHERE 条件子句中指定集团字段了。

TABLES spfli.
SELECT SINGLE * FROM
spfli CLIENT SPECIFIED WHERE mandt BETWEEN '100' AND '999'
.
WRITE
spfli-cityto.

禁止使用缓冲 BYPASSING BUFFER ...

可以在数据字典 Technical settings 中进行设定。如果对已经进行了缓冲设定的数据库表,系统将在首次数据查询操作的同时进行数据缓冲。不同的缓冲之间或缓冲与数据库表本身间的同步过程则数据接口完成。

SELECT 语句中的 FROM 子句的 BYPASSING BUFFER 附加项,可以用于在特殊情况下禁止缓冲。此外, DISTINCT 附加项与联合查询、总计选择、 IS NULL 条件、子查询,以及 GROUPBY ORDER BY 同时使用时,也将自动忽略缓冲设定

SELECT ... FROM <tables> BYPASSING BUFFER ...

"> 限定选择行数 UP TO n ROWS ...

SELECT ... FROM <tables> UP TO <n> ROWS ...

如果 n 为正数,系统最多选择 n 行到程序中,如果 n 0 ,则还是选择所有满足条件的数据。如果同时使用 ORDER BY 选项,则系统首先选出所有满足条件的数据,并排序,然后将头 n 行作为选择结果。

注:一般使用 SINGLE 是表示根据表的关键字来查询,这样才能确保只有一条数据,所以 当使用 SINGLE 时,不能再使用 ORDER BY 语句 (因为也没有必要了),如果查询时不是根据关键字来查询,且查询时先排序再取一条时,我们只能使用另一种语法:

SELECT * FROM tj02t INTO CORRESPONDING FIELDS OF TABLE gt_result UP TO 1 ROWS WHERE SPRAS = 'E'

ORDER BY ISTAT .

SELECT 语句嵌套

DATA : wa_carrid TYPE spfli-carrid,
wa_connid
TYPE
spfli-connid,
wa_carrname
TYPE
scarr-carrname.

SELECT carrid connid FROM spfli INTO (wa_carrid, wa_connid) WHERE

cityfrom =
'NEW YORK' .
SELECT carrname FROM scarr INTO
wa_carrname
WHERE
carrid = wa_carrid.
WRITE
wa_carrname.
ENDSELECT
.
ENDSELECT
.

每当在表 SPFLI 中查询到一个符合条件值,系统就重新对 SCARR 进行一次查询,这 与标准 SQL 相关子查询类似

FOR ALL ENTRIES 选项

SELECT ... FOR ALL ENTRIES IN <itab> WHERE <cond> ...

该选项拿 <itab> 每一条数据到数据库表中查询,并且将查询出来的结果集使用合并并返回。重复的数据会自动剔除。 如果 <itab> 为空,则会忽略该条件(即使 Where 后面还有其它条件,所有的条件都会忽略),即查询出所有数据 <cond> 条件语句中 不能使用 LIKE , BETWEEN , IN 这些操作符,只能使用 比较操作符

该选项的作用与 Selection Options 是类似的

为了避免多次重复数据查询( 嵌套查询 效率很低),可以先将 SPFLI 中符合条件的数据选进一个内表,然后仅根据该内表中包含的 carrid 字段继续查询表 scarr 这样最多只发送两次查询,而不是 1+N 次查询

选出符合已存在内表中所有满足条件的数据值:

FOR ALL ENTRIES 对于以下两种情况会忽略 SAP bufferung

l Tables with single record buffering .

l Tables with generic buffering if the condition after FOR ALL ENTRIES prevents precisely one generic area from being specified exactly.

1、 使用该选项后,对于最后得出的结果集系统会自动删除重复行( 具有 DISTINCT 选项的作用,但与 DISTINCT 不同的是,这个去除重复的动作是在将所有数据从数据库中讲到到 SAP 应用服务器后进行删除的,而不是在数据库系统里去除的,这是因为 FOR ALL ENTRIES 在执行查询时,可能会分成多个 SQL 语句,所以只能将多次读取的数据合并后进行去除 )。因此如果你要保留重复行记录时,记得在 SELECT 语句中添加足够键值项目(有必要时,增加所有所查表的所有主键),以保证结果集中所需重复项目不会被删除。

2、 FOR ALL ENTRIES IN 后面使用的内部表 itab 如果为空,系统将视为无条件选取(即忽略 WHERE 语句),将当前 CLIENT 下所有记录选出,因此为避免无意义的全件检索,在使用该语句前一定要判断内部表 itab 是否为空,为空时不执行包含该语句的数据库检索处理

3、 由于 itab-f 实际上是作为占位符被替换,所以内部表 itab 中不要带 HEADER 行,以免造成混淆,检索出错

4、 内表中的条件字段不能使用 LIKE,BETWEEN,IN 比较操作符 。因为这些比较操作符都是不确定比较操作符(将选择条件设定在一个范围内),而 FOR ALL ENTRIES IN 语句的作用相当于将选择条件块全部并列开来,用 OR 连接,如果每个 OR 分支中又是不确定的范围,那么系统性能将大大降低,因此 R/3 系统在使用该语句时禁止使用不确定比较操作符。

5、 使用该语句时, ORDER BY 语句和 HAVING 语句 将不能使用。

6、 使用该语句时, COUNT( * ) (并且如果有了 COUNT 函数,则不能再选择其他字段,只能使用在 Select ... ENDSelect 语句中了)以外的 所有合计函数( MAX,MIN,AVG,SUM )都不能使用

DATA : tab_spfli TYPE TABLE OF spfli ,
tab_sflight
TYPE SORTED TABLE OF sflight WITH UNIQUE KEY TABLE LINE ,
wa
LIKE LINE OF tab_sflight .
SELECT carrid connid
INTO CORRESPONDING FIELDS OF TABLE
tab_spfli
FROM
spfli
WHERE cityfrom = 'NEW YORK' .

SELECT carrid connid fldate
INTO CORRESPONDING FIELDS OF TABLE
tab_sflight
FROM
sflight
FOR ALL ENTRIES IN
tab_spfli
WHERE carrid = tab_spfli - carrid AND connid = tab_spfli - connid .

LOOP AT tab_sflight INTO wa .
AT NEW connid .
WRITE : / wa - carrid , wa - connid .
ENDAT .
WRITE : / wa - fldate .
ENDLOOP .

注意: FOR ALL ENTRIES 所使用的内表里一定要有数据,否则查询时会将表中的所有数据都查询出来, 即使 Where 后面还有其它条件,所有的条件都会忽略

SELECT vbeln posnr pstyv werks matnr arktx lgort waerk kwmeng
FROM
vbap
INTO TABLE
gt_so
FOR ALL ENTRIES IN
lt_matnr
WHERE matnr = lt_matnr - matnr AND vbeln IN s_vbeln AND posnr IN s_posnr
.

如果上面的 lt_matnr 为空,则“ AND  vbeln IN s_vbeln AND posnr IN s_posnr ”条件也会忽略掉,即整个 Where 都会被忽略掉。

TABLES : mara .
DATA : BEGIN OF strc OCCURS 0 ,
matnr
LIKE mara - matnr ,
lvorm
LIKE mara - lvorm ,
END OF strc .
SELECT - OPTIONS : s_matnr FOR mara - matnr .
START
- OF - SELECTION .
SELECT matnr FROM mara
INTO CORRESPONDING FIELDS OF TABLE
strc
FOR ALL ENTRIES IN strc WHERE matnr = strc - matnr
.

当内表 strc 为空时,上面 Select 语句生成的标准 SQL 如下:

SELECT

"MATNR"

"MARA"

WHERE"MANDT" = '210'

内表 strc 不为空时,生成的 SQL 如下:

strc - matnr = '000000000000000101' .
APPEND strc .
strc
- matnr = '000000000000000103' .
APPEND strc .
strc
- matnr = '000000000000000104' .
APPEND strc .
START
- OF - SELECTION .
SELECT matnr FROM mara
INTO CORRESPONDING FIELDS OF TABLE
strc
FOR ALL ENTRIES IN strc WHERE matnr = strc - matnr
.

SELECT

"MATNR"

"MARA"

WHERE "MANDT" = '210' AND "MATNR" IN ( '000000000000000101' , '000000000000000103' , '000000000000000104' )

注:这里看上去 FOR ALL ENTRIES 使用 IN 表达式来代替了,这是只有使用到内表中一个条件是这样的,如果使用多个条件时,不会使用 In 表达式,而是使用 OR 连接,像这样:

另外,在使用 FOR ALL ENTRIES 时,不管使用了条件内表中的一个还是多个条件字段,都会以 5 个值为单位进行 SQL 发送,但 RANGE 可以达到 3000 左右

内表 strc 不为空时,且在 Where 后面加上了另外的条件:

strc - matnr = '000000000000000101' .
APPEND strc .
strc
- matnr = '000000000000000103' .
APPEND strc .
strc
- matnr = '000000000000000104' .
APPEND strc .
START
- OF - SELECTION .
SELECT matnr FROM mara
INTO CORRESPONDING FIELDS OF TABLE
strc
FOR ALL ENTRIES IN strc WHERE matnr = strc - matnr and ERNAM = 'XUJIJING'
.

SELECT

"MATNR"

"MARA"

WHERE "MANDT" = '210' AND "MATNR" IN ( '000000000000000101' , '000000000000000103' , '000000000000000104' ) AND "ERNAM" = 'XUJIJING'

内表 strc 为空时,且在 Where 后面加上了另外的条件:

SELECT

"MATNR"

"MARA"

WHERE "MANDT" = '210'

使用 JOIN 时,会绕过 SAP 缓存,可以使用 FOR ALL ENTRIES 来代替

ON 后面的条件与 Where 条件类似,但有以下不同:

ü 必需要有 ON ,也就是说必须要有一个条件,且 多个条件之间只能使用 AND 连接

ü 所有 条件表达式中两个操作数之间 必须有一个是来自于 JOIN 右边表 dbtab_right 的某个字段

ü 不能使用 NOT , LIKE, IN (但如果是 INNER JOIN ,则 > < BETWEEN …AND <> 都可用)

ü 如果是 INNER JOIN ,也可把 ON 从句中的 <cond> 条件置于 WHERE 子句中 Left Outer Join 是有所区别的,请参见后面),因为二者效果一样

ü 如果是 LEFT OUTER JOIN ,则只能使用等号操作符: ( =, EQ )

ü 如果是 LEFT OUTER JOIN ,则 至少有一个条件表达式的两个操作数一个是来自于左边表 dbtab_left 另一个来自右边表 dbtab_right

ü 如果是 LEFT OUTER JOIN ,则 只能与同一个 表进行连接(关联) ,即每个条件表达式两个操作数中除了一个已固定来自于右边表 dbtab_right 外(不管是 Inner ,还是 Left ,因为所有 ON 后面每个条件中必须要有一个是来自于右边表 dbtab_right 中的字段),所有条件表达式中另外一个操作数(在另一操作数非 dbtab_right 字段的情况下,如果另一操作数就是 dbtab_right 某个字段或某个常量时,就另当别论了)必须来自于同一个左边表 dbtab_left ,即 LEFT OUTER JOIN 只能与一个 dbtab_left 发生关联关系(即在 ON 条件中所有表达试中的左表只能出现同一个,不能出现第二个,如下所示:)

ü 如果是 LEFT OUTER JOIN 同一右表 dbtab_right 不能出现在不同的 LEFT OUTER JOIN (在一个 Select 语句中 LEFT JOIN 可以多次出现,关联不同的表)的条件表达式中:

ü LEFT OUTER JOIN 右边表 dbtab_right 的所有字段不能出现在 WHERE 中( 如果出现在 WHERE 从句好像没有什么意义了,此时与 INNER JOIN 相当),只能出现在 ON 条件从句中 可以将从表的条件写在 On 后面而不是 Where 后面来解除这个限制 ,但是, Left join 的条件放在 on where 后面是不一样的 请看后面的示例

SELECT ...FROM <tab> [INNER] JOIN <dbtab> [AS <alias>] ON <cond>

DATA : BEGIN OF wa ,
carrid
TYPE spfli - carrid ,
connid
TYPE spfli - connid ,
fldate
TYPE sflight - fldate ,
bookid
TYPE sbook - bookid ,
END OF wa ,
itab
LIKE SORTED TABLE OF wa WITH UNIQUE KEY carrid connid fldate bookid .
SELECT p ~ carrid p ~ connid f ~ fldate b ~ bookid
INTO CORRESPONDING FIELDS OF TABLE
itab
FROM ( ( spfli AS
p
INNER JOIN sflight AS f ON p ~ carrid = f ~ carrid AND p ~ connid = f ~ connid )

INNER JOIN sbook AS b ON b ~ carrid = f ~ carrid AND b ~ connid = f ~ connid AND b ~ fldate = f ~ fldate )
WHERE p ~ cityfrom = 'FRANKFURT' AND p ~ cityto = 'NEW YORK' AND f ~ seatsmax > f ~ seatsocc .

SELECT ...FROM <tab> LEFT [OUTER] JOIN <dbtab> [AS <alias>] ON <cond>

DATA : BEGIN OF wa ,
carrid
TYPE scarr - carrid ,
carrname
TYPE scarr - carrname ,
connid
TYPE spfli - connid ,
END OF wa ,
itab
LIKE SORTED TABLE OF wa WITH NON-UNIQUE KEY carrid .
SELECT s ~ carrid s ~ carrname p ~ connid
INTO CORRESPONDING FIELDS OF TABLE
itab
FROM scarr AS
s
LEFT OUTER JOIN spfli AS p ON s ~ carrid = p ~ carrid AND p ~ cityfrom = 'FRANKFURT'
.

数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。在使用 left jion 时, on where 条件的区别如下:

1 on 条件是在生成临时表时使用的条件,它不管 on 中的条件是否为真,都会返回左边表中的记录

2 where 条件是在临时表生成好后,再对临时表进行过滤的条件。 这时已经没有 left join 的含义 (必须返回左边表的记录)了,条件不为真的就全部过滤掉。

假设有两张表:

1 tab1

其实以上结果的关键原因就是 left join ,right join,full join 的特殊性, 不管 on 上的条件是否为真都会返回 left right 表中的记录 full 则具有 left right 的特性的并集。而 inner join 没这个特殊性,则条件放在 on 中和 where 中,返回的结果集是相同的( 但放在 ON 后面的效率会高一些

使用子查询时会绕过 SAP buffering

有时只需从一个表中选择数据,但这些数据与其他表中的数据字段内容相关联,但又不需将其它表中相关的数据查出来,类似操作可通过子查询进行。子查询是没有 SINGLE , INTO , and ORDER BY 的查询语句,通过 col operator [ALL|ANY|SOME] EXISTS IN 连接至 WHERE 从句与 HAVING 从句中 (但不能出现在 ON 从句中):

l col operator [ ALL | ANY | SOME ] ( SELECT …FROM … )

l [ NOT ] EXISTS ( SELECT …FROM … )

l [ NOT ] IN ( SELECT …FROM … )

子查询可以嵌套,但不能用于 pool tables cluster tables 两种表

ALL ANY SOME 子查询

这种子查询的 SELECT 从句中只有一个表字段或者是一个统计列

... <s> <operator> <subquery> ...

要求子查询只能返回一条数据(否则运行时出错),所以 <subquery> 一般是一个 SELECT SINGLE 的子查询。

如果子查询的结果只有一条数据时,可以省略 [ ALL | ANY | SOME ] 选项:

DATA wa_sflight TYPE sflight.
SELECT
*
FROM
sflight
INTO
wa_sflight
WHERE
seatsocc = ( SELECT MAX ( seatsocc ) FROM sflight ).
ENDSELECT
.

如果子查询返回的是多条,则可以使用以下子查询:

... <s> <operator> ALL|ANY|SOME <subquery> ...

l 如果使用 ALL ,则如果子查询返回的所有行都满足 <operator> 比较条件时,才为真

l 如果使用 ANY|SOME ,则如果子查询返回的所有行中只要有一条满足 <operator> 比较条件时,就会为真

l 比较操作符 (= or EQ) ANY|SOME 一起使用时,与 IN 操作符具有一样的效果

DATA : id TYPE sbook-customid,
cnt TYPE i .
SELECT customid COUNT
( * )
FROM
sbook
INTO
(
id , cnt )
GROUP BY customid
HAVING COUNT
( * ) >= ALL ( SELECT COUNT ( * )
FROM
sbook
GROUP BY
customid ).
ENDSELECT
.

[NOT] IN 子查询

此类子查询 SELECT 从句中也只有单独的一列选择列,但查询出的结果可能有多个。与 [NOT] IN 一起使用

DATA : carr_id TYPE spfli-carrid VALUE 'LH' ,
conn_id
TYPE spfli-connid VALUE '0400'
.
DATA : city TYPE
sgeocity-city,
lati
TYPE p DECIMALS 2
,
longi
TYPE p DECIMALS 2
.
SELECT SINGLE
city latitude longitude
INTO
(city, lati, longi)
FROM
sgeocity
WHERE
city IN ( SELECT cityfrom FROM spfli
WHERE
carrid = carr_id
AND
connid = conn_id ).
WRITE
: city, lati, longi.

[NOT] EXISTS 子查询

这类子查询没有返回值,也不要求 SELECT 从句中只有一个选择列,选择列可以任意个数, WHERE or HAVING 从句来根据该子查询的是否查询到数据来决定外层主查询语句来选择相应数据

该类子查询只能与 [NOT] EXISTS 一起使用:

DATA : name_tab TYPE TABLE OF scarr-carrname,
name
LIKE LINE OF
name_tab.
SELECT carrname INTO TABLE name_tab FROM
scarr
WHERE
EXISTS ( SELECT * FROM spfli
WHERE
carrid = scarr~carrid
AND cityfrom = 'NEW YORK' ).
LOOP AT name_tab INTO
name.
WRITE
: / name.
ENDLOOP
.

此子查询又为 相关子查询

如果某个子查的 WHERE 条件中引用了外层查询语句的列,则称此子查询为 相关子查询 。相关子查询对外层查询结果集中的每条记录都会执行一次,所以尽量少用相关子查询

SELECT <lines><agg>( [DISTINCT] <s 1 > ) [AS <a 1 >]<agg>( [DISTINCT] <s 2 > ) [AS <a 2 >] ...

MAX MIN AVG SUM COUNT

聚合函数都可以加上 DISTINCT 选项

MAX, MIN, or SUM 计算的结果类型为相应表字段的类型

AVG 计算的结果类型为数据词典类型 FLTP, ABAP 程序里可以使用 F 类型来接收

COUNT have the Dictionary type INT4.

DATA : BEGIN OF luggage,
average
TYPE p DECIMALS 2
,
sum TYPE p DECIMALS 2
,
END OF
luggage.
SELECT AVG ( luggweight ) AS average SUM ( luggweight ) AS sum

INTO CORRESPONDING FIELDS OF
luggage
FROM
sbook.

SELECT carrid connid SUM ( seatsocc )
FROM
sflight
INTO
(wa_carrid, wa_connid, sum_seat)
WHERE spfli~cityform =
`sing`

SELECT <lines><s 1 > [AS <a 1 >] <s 2 > [AS <a 2 >] ...

<agg><s m > [AS <a m >] <agg><s n > [AS <a n >] ...

GROUP BY <s 1 ><s 2 > ....

GROUP BY 后面还可以是动态指: ... GROUP BY (<itab>) ... 这里的 <itab> 是一个具有列宽为 72 C Type 的内表,只有一个字段,且存储了分组的字段名,如上面的 <s1><s2>

如果将统计函数与 GROUP BY 子句一起使用,那么 Select 语句中未出现在统计函数的数据库字段都必须在 GROUP BY 子句中出现(如这里的 <s1> <s2> )。如果使用 INTO CORRESPONDING FIELDS 项,则需要在 Select 语句中通过 AS 后面的别名将统计结果存放到与之相应同名的内表字段中。

DATA : carrid TYPE sflight-carrid,
minimum
TYPE p DECIMALS 2
,
maximum
TYPE p DECIMALS 2
.
WRITE : / `Carrier ID`
,
AT 15 `Minimum Price`
,
AT 30 `Maximum Price`
.

SELECT carrid MIN ( price ) MAX
( price )
INTO
(carrid,minimum,maximum)
FROM
sflight
GROUP BY
carrid.
WRITE
: / carrid, minimum, maximum.
ENDSELECT
.

Carrier ID    Minimum Price  Maximum Price

LH            899.00          6,000.00

SQ            849.00            849.00

只有使用 GROUP BY 子句时,才能出现 HAVING

SELECT <lines><s1> [AS <a1>] <s2> [AS <a2>] ...

<agg><sm> [AS <am>] <agg><sn> [AS <an>] ...

GROUP BY <s1><s2> ....

HAVING <cond>.

WHERE 动态 条件从句 一样,也可以使用动态的条件

SELECT carrid min ( price ) as m max ( price )
INTO
(carrid, minimum, maximum)
from
sflight
group by
carrid
HAVING MAX ( price )  > 10
.
WRITE
: / carrid, minimum, maximum.
ENDSELECT
.

注: SELECT SIGLE 不能与 ORDER BY 一起使用

根据关键字排序:

SELECT  *

... ORDER BY PRIMARY KEY .

注: ... ORDER BY PRIMARY KEY 只能用在从单表中查询时使用

指定排序字段,默认为升序:

SELECT ...

ORDER BY <s1> [ASCENDING|DESCENDING]

<s2> [ASCENDING|DESCENDING] ...

动态指定排序列:

SELECT ...

ORDER BY ( column_syntax) .

column_syntax Initial 时,则会忽略

DATA : BEGIN OF wa ,
carrid
TYPE sflight - carrid ,
connid
TYPE sflight - connid ,
min
TYPE i ,
END OF wa .
SELECT carrid connid MIN ( seatsocc ) AS min
INTO CORRESPONDING FIELDS OF wa
FROM
sflight
GROUP BY
carrid connid
ORDER BY carrid min DESCENDING .

WRITE : / wa - carrid , wa - connid , wa - min .
ENDSELECT .

通过 游标 读取数据

在通常的 SELECT 语句中,满足查询条件的所有数据都是一次性从数据库中读取出来放在 INTO 子句指定的目标区域中,这样做会带来一个问题:这些数据从数据库读取出来然后一条条 APPEND 到内表中,这一过程也需要花费时间。如果使用游标,则会解决 SELECT 语句的一些处理开销,使用游标时,可以将数据行直接放到一个扁平的结构体对象中。

1. 打开游标

OPEN CURSOR [WITH HOLD] <c> FOR SELECT <result>

FROM <source>

[WHERE <condition>]

[GROUP BY <fields>]

[HAVING <cond>]

[ORDER BY <fields>].

游标不能用于 SINGLE 选项的 SELECT 语句。在数据库提交时,系统将自动关闭光标,一个例外情况是如果在 OPEN CURSOR 语句中使用了 WITH HOLD 选项时,则 Native SQL 数据库提交将不会关闭光标。

<C> 必须是一个使用 DATA 语句定义的具有 CURSOR 类型的变量

2. 读取数据

FETCH NEXT CURSOR <c> INTO <target>.

<target> 为扁平的结构对象

如果读取到数据,则 SY-SUBRC to 0, otherwise to 4. After a FETCH statement, the system field SY-DBCNT contains the number of lines already read by the current cursor.

3. 关闭游标

CLOSE CURSOR <c>.

DATA : c TYPE cursor .
DATA : wa TYPE
spfli.
" 1
打开游标
OPEN CURSOR : c FOR SELECT carrid connid
FROM
spfli
WHERE carrid = 'LH'
.
DO
.
" 2
、读取数据
FETCH NEXT CURSOR c INTO CORRESPONDING FIELDS OF wa.
IF sy-subrc <> 0
.
" 3
、关闭游标
CLOSE CURSOR c .
EXIT
.
ELSE
.
WRITE
: / wa-carrid, wa-connid.
ENDIF
.
ENDDO
.

INSERT

INSERT

{ INTO {dbtab|( dbtab_syntax )} [ CLIENT SPECIFIED ] VALUES wa}

| {{dbtab|( dbtab_syntax )} [ CLIENT SPECIFIED ] FROM wa|{ TABLE itab [ ACCEPTING DUPLICATE KEYS ]}}

插入单条 :

INSERT INTO <tabname> VALUES <wa>.

INSERT <tabname> FROM <wa> .

INSERT <tabname>.

在插入时是按照数据库表结构来解析 <wa> 结构,与 <wa> 中的字段名无关,所以 <wa> 的长度只少要等于或大于所对应表结构总长度

工作区 wa 是与数据表具有相同结构的数据对象,一般直接基于数据表结构声明 FROM 这种格式在以下这种情况下可以省略( 此方式下不允许使用动态指定表 ):

TABLES : mard .
INSERT mard .

如果插入成功, SY-SUBRC 0 ,否则为 4

TABLES spfli .
DATA wa TYPE spfli .
wa
- carrid = 'LH' .
wa
- cityfrom = 'WASHINGTON'
.
INSERT INTO spfli VALUES wa .


wa
- carrid = 'UA' .
wa
- cityfrom = 'LONDON'
.
INSERT spfli FROM wa .


spfli
- carrid = 'LH' .
spfli
- cityfrom = 'BERLIN'
.
INSERT spfli .

插入多行 :

INSERT dbtab FROM TABLE itab .

其中内表 itab 中包含希望插入的数据条目,该 内表的行结构也必须和数据库表的行结构一致 。如果所有数据都插入成功,则 SY-SUBRC 返回 0 ,如果有一条不成功,则所有数据都不会插入,但使用下面的插入语句可以避免部分不成功的问题:

INSERT <tabname> FROM TABLE <itab> [ACCEPTING DUPLICATE KEYS] .

其中 ACCEPTING DUPLICATE KEYS 选项的效果是: 如果现出关键字相同条目,系统将 SY-SUBRC 返回 4 ,并跳过该条目,但其他数据会插入进去。

SY-DBCNT 存储了插入成功的条数,而不管 SY-SUBRC 的值是什么

DATA : itab TYPE HASHED TABLE OF spfli WITH UNIQUE KEY carrid connid ,
wa
LIKE LINE OF itab ..
" 假设下面两条数据在数据库中都不存在
wa
- carrid = 'UD' .
wa
- connid = '0011' .
wa
- cityfrom = 'ZH' .
INSERT wa INTO TABLE itab
.
INSERT spfli FROM TABLE itab .
WRITE : / sy - subrc , sy - dbcnt .

wa
- carrid = 'AD' .
wa
- connid = '4574' .
wa
- cityfrom = 'EN' .
INSERT wa INTO TABLE itab
.

INSERT spfli FROM TABLE itab ACCEPTING DUPLICATE KEYS .
WRITE : / sy - subrc , sy - dbcnt .

UPDATE

UPDATE {dbtab|( dbtab_syntax )} [ CLIENT SPECIFIED ]
{ {
SET [ col1 = f1 col2 = f2 ... ]
[col1 = col1 + f1 col2 = col2 + f2 ...]
[col1 = col1 - f1 col2 = col2 - f2 ...]
[(expr_syntax1) (expr_syntax2) ...] [
WHERE
sql_cond] }
| {
FROM wa|{ TABLE
itab} } }

expr_syntax1 :动态指定修改表达式,可以是 character-like data object or a standard table ,如果为 Initail 则忽略

sql_cond :请参考 SELECT 语句中的 WHERE 从句用法(但不适用 FOR ALL ENTRIES 条件)

更新单条:

UPDATE dbtab FROM wa.

系统将根据工作区中 表关键字段 primary key (而不是内表行的关键字段)的值定位数据行,并使用所有非关键字段的值将该行进行更新。

工作区 wa 的宽度必须大于或等于数据库表宽度,会按照数据库表结构从前往后来解析 wa 各字段,与名称无关

如果数据库中有一个 wa primary key 相同的数据,则 SY-SUBRC 0 ,否则 SY-SUBRC 4

TABLES :mard.
UPDATE
mard.

注:此方式不能动态指定表名

"CARRID CONNID spfli 的主键

DATA wa TYPE spfli .
MOVE 'AA' TO wa - carrid .
MOVE '0064' TO wa - connid .
MOVE 'WASHINGTON' TO wa - cityfrom
.
UPDATE spfli FROM wa .

TABLES spfli .
MOVE 'LH' TO spfli - carrid .
MOVE '0017' TO spfli - connid .
MOVE 'BERLIN' TO spfli - cityfrom
.
UPDATE spfli .

更新多行:

UPDATE dbtab FROM TABLE itab.

对于每一个内表行,都根据表关键字段要更改的数据,并更改其他非关键字段的值,如果所有内表行都更新成功, SY-SUBRC 返回 0 只要有一条未成功,则返回 4 (但没有问题的数据行还是会被更新) S Y-DBCNT 存储了被修改的行数。如果内表 itab 为空,则 SY-SUBRC and SY-DBCNT 都会为 0

"CARRID CONNID spfli 的主键

DATA : itab TYPE HASHED TABLE OF spfli WITH UNIQUE KEY carrid connid ,
wa
LIKE LINE OF itab .
wa
- carrid = 'UA' .
wa
- connid = '0011' .
wa
- cityfrom = 'ZH' .
INSERT wa INTO TABLE itab .
wa
- carrid = 'LH' .
wa
- connid = '1245' .
wa
- cityfrom = 'EN' .
INSERT wa INTO TABLE itab
.
UPDATE spfli FROM TABLE itab .

更新指定列:

UPDATE dbtab SET f1 = g1 … fi = gi WHERE <conditions>.

WHERE 子句后面可以是所有表关键字段,也可以不是,但如果只是更新一条,则条件需要使用所有表关键字段。 SET 子句中只能为非关键字段。对于数据字段, f = g 外,还可以使用 f = f + g f = f - g 两种形式在该字段原有值基础上增减。如果至少有一条数据被更新,则 SY-SUBRC 返回 0 ,否则返回 4 SY-DBCNT 中存储了修改的行数

注:此种方式不能动态指定表

UPDATE sflight
SET planetype = 'A310' price = price - '100.00'

WHERE carrid = 'LH' AND connid = '0402' .

DELETE

DELETE { { FROM {dbtab|( dbtab_syntax )} [ CLIENT SPECIFIED ] [ WHERE sql_cond] }

| { {dbtab|( dbtab_syntax )} [ CLIENT SPECIFIED ] FROM wa|{ TABLE itab} } }.

sql_cond :请参考 SELECT 语句中的 WHERE 从句用法(但不适用 FOR ALL ENTRIES 条件)

DELETE dbtab FROM wa.

从数据库表中删除表关键字段与工作区 wa 中相应字段相同的行。工作区的长度至少要等于数据库表的产关键字段长度。

如果数据库中存在这样的数据,则 SY-SUBRC 0 ,否则 SY-SUBRC 4

或者省略 wa

TABLES :mard.
DELETE
mard.

注:此方式不能动态指定表名

"CARRID CONNID spfli 的主键
DATA : BEGIN OF wa ,
carrid
TYPE spfli - carrid ,
connid
TYPE spfli - connid ,
END OF wa .
MOVE 'AA' TO wa - carrid .
MOVE '0064' TO wa - connid
.
DELETE spfli FROM wa .

TABLES spfli .
MOVE 'LH' TO spfli - carrid .
MOVE '0017' TO spfli - connid
.
DELETE spfli .

DELETE dbtab FROM TABLE itab.

根据表主键来删除 itab 中相同的数据行

如果所有行都被删除, SY-SUBRC 0 ,否则为 4 SY-DBCNT 存储了被删除的条数。如果内表 itab 为空,则 SY-SUBRC SY-DBCNT 都为 0

"CARRID CONNID spfli 的主键
DATA : BEGIN OF wa ,
carrid
TYPE spfli - carrid ,
connid
TYPE spfli - connid ,
END OF wa ,
itab
LIKE HASHED TABLE OF wa WITH UNIQUE KEY carrid connid .
wa
- carrid = 'UA' .
wa
- connid = '0011' .
INSERT wa INTO TABLE itab .

wa
- carrid = 'LH' .
wa
- connid = '1245' .
INSERT wa INTO TABLE itab
.
DELETE spfli FROM TABLE itab .

删除单行或多行

DELETE FROM dbtab WHERE <conditions>.

If at least one line is deleted, the system sets SY-SUBRC to 0, otherwise to 4. SY-DBCNT contains the number of lines deleted.

DELETE FROM sflight WHERE planetype = 'A310' AND carrid = 'LH' .

MODIFY (插入或修改)

MODIFY {dbtab|( dbtab_syntax )} [ CLIENT SPECIFIED ] FROM wa|{ TABLE itab}.

MODIFY 相当于 INSERT UPDATE 的结合。当使用 MODIFY 语句操作数据库时,如果程序中指定的数据行已经存在数据库中(根据表关键字判断)则对其进行更新操作,如果不存在,则进行插入操作。

插入或更新单行

MODIFY dbtab FROM wa.

根据工作区 wa 中的表关键字段来定位要修改的数据,如果不存在,则插入。工作区 wa 的宽度也要大于或等于表结构的所有字段表宽度

MODIFY <dbtab>.

SY-SUBRC 总是为 0

插入或更新多行

MODIFY dbtab FROM TABLE itab .

根据内表中的每一行表关键字段来查找数据是否存在,如果不存,则会插入。

SY-SUBRC 总是为 0 SY-DBCNT 为内表行数

使用表工作区 Interface work areas

表工作区是接口工作区的一种 使用 TABLES 语句声明

TABLES dbtab.

该语句会根据数据库中 视图 、或 ABAP Dictionary 中定义的 structure 名为 dbtab 的表来创建一个同名的结构 组成该结构的内表字段类型与参考的表 或视图、 structure 相同。

4.0 版以前 使用 Open SQL 访问表时 一定要先使用该语句来声明 Interface work areas 现在不需要了 但是 你仍然需要使用该语句来使屏幕 input/output 字段参考相应的数据库表 PBO 事件里 ABAP 程序会将字段内容传递到相应的屏幕字段中 PAI 事件时 屏幕上的字段会存储到 ABAP 程序中对应 Interface work areas 中的相应字段中 具体可以参照《 User Dialogs.docx 》中的 < 示例 屏幕元素自动参考数据词典 > 章节

在指定了表工作区之后,在使用 SELECTSINGLE 语句时可以省略 INTO 子句( INTO TABLE 时不能省略),系统默认将数据行读取到同名变量的表工作区中。

TABLES spfli.
SELECT SINGLE * FROM spfli WHERE cityfrom = 'NEW YORK'
.
WRITE
: spfli-cityto.

操作时间分析(类似 System.currentTimeMillis() 方法)

GET RUN TIME FIELD <f>.

其中 f 应为 I 类型的字段,系统返回从程序开始后的毫秒数。


DATA : t1 TYPE i ,
t2
TYPE i
,
t
TYPE i
.
GET RUN TIME FIELD
t1.
TABLES
: spfli.
SELECT SINGLE * FROM spfli CLIENT SPECIFIED WHERE mandt BETWEEN '100' AND '999'
.
GET RUN TIME FIELD
t2.
t = t2 - t1.
WRITE
: / t.

如果需要全面性能分析,可以使用 SE30

数据库优化

1、 优先使用多表联接,不使用内嵌 Select

2、 Joins 方式联合查询不会使用到缓存 所以尽量少用 可以使用 ABAP Dictionary view 来替换。创建 ABAP Dictionary view 来代替从物理表中读取

3、 使用内表 FOR ALL ENTRIES 查询选项代替内嵌 Select FOR ALL ENTRIES 是一种与联结和数据库视图相似的多表操作方式 但是其性能不如联结和数据库视图好。

4、 INSERT, UPDATE, and DELETE 时,使用内表的方式来减少与数据库交互次数

5、 使用 游标读取数据 ,这样省掉了将从数据库中的取记录放入内表的 INTO 语句这一过程开销。

6、 避免全表扫描,在 WHERE orHAVING 从句中 使用索引字段进行查询。

R/3 System 会自动为 primary index( 主键索引 也叫第一索引 ) 创建 UNIQUE 主键索引。如果在 WHERE orHAVING 从句中未使用到 primary index fields ,则不会使用到主键索引。此时 可以通过 ABAP Dictionary 来创建第二索引(可以是 unique 的,也可以是非唯一索引)

在创建索引时,索引字段不能在其他索引中使用过( 索引字段不要重叠 ,否则性能会很差。另外,只能那些经常读取的表进行创建索引,而那些经常需要更新的表,则不适合创建索引,因为在数据更新时同时会更新索引。最后,索引创建时不多于 4 个字段,一个表上的创建的索引不要超过 5 个。

在需要经常访问的字段上创建索引,并且 将重复数据最少的字段放在索引最开头的规则来确定索引中字段顺序 要在重复率少的字段上创建索引,而不应该在重复率高的字段上创建索引

7、 除了主键字段,如果一个表的某些字段被 SELECT 语句频繁访问,则考虑基于这些字段建立第二索引。注:一个表的不是越多越好,使用创建不当的第二索引进行查询可能导致比不使用该索引进行查询更差的性能

8、 组成索引的所有字段都要用到,并且使用的是“ = ”表达式,而且各个表达式使用“ AND ”连接时,查找效率会最高。

9、 在条件从句中不要使用否定的逻辑表达式,如 NE(<>) NOT LIKE ,因为这样数据库不会使用索引,所以要使用肯定操作比较操作符,如 EQ LIKE 。另外,如果可能,避免在 WHERE 从句中使用 NOT 操作符,因为数据库不会用到索引。

10、 不要使用 OR 来连接多个索引字段表达式, OR 后面的表达式中的字段将不会用到索引 ,看是否能使用 IN 操作符转换

11、 使用部分索引要注意的问题:如果一个索引是由多个字段组成的,你可以只使用一部分字段来进行查询时,也可以使用到索引,但使用时要注意要按照索引定义的顺序来使用,如定义时为 a,b,c,d,e 五个字段组成的索引,当使用 c,d 来查询时,查询条件前面也一定要带上 a,b 查询条件字段,所以当只按 c,d 两个字段查询,是用不到索引的 好像只要按物理顺序写,是可以用到索引的???

12、 WHERE 条件语句中,要将索引字段条件写在最前面(左边),非索引字段写在最后面 ,这样查询时效率会高些

13、 最好不要将允许为 NULL 值的字段作为索引字段,因为某些数据库不会将 NULL 值存储到索引表中。

14、 一般不要使用 ORDER BY 除非 ORDER BY 所使用的索引与 WHERE 语句所使用的索引一样。此时可以将数据读到内表后,对内表进行排序

使用 Buffer Tables

你可以通过访问服务器上的 Buffer Tables 来最大限度的减少访问数据的时间。

是否是缓存表,可以在 ABAP Dictionary 中来配置:

表的三种缓冲类型:

l Partial buffering ( 单记录缓存 single entry) :从数据库中仅读取一条数据并存储到 table buffer 中。此缓存只对 SELECT SINGLE… 语句起作用,如果不是则绕过单记录缓存区,直接访问数据库表。

l Generic buffering( 普通缓存,区域缓存 ) 在此种情况下,你需要在指定一个 generic key ( 由部分或全部关键字段组。如果主键是由一个字段构成,则不能选择此类型缓存。下面的 generic key 为前面三个主键: MANDT CARRID COUNTNUM) 。表的内容会被划分到相应的 generic areas 中(根据 WHERE 条件中使用的主键字段),当你使用 generic keys 进行数据访问时,则属于此 generic area 的整片数据都会被加载到 table buffer 中。客户特定的表一般根据 client 来缓存。 此类型的缓存需注意:

1、 查询时如果使用 BYPASSING BUFFER 选项,除了绕过缓存直接到数据库查询外,查询出来的数据也不会放入缓存。

2、 只要查询条件中出现了用作缓存区域的所有字段 (generic key) ,则查询出所有满足 generic key 的所有数据,然后进行缓存。

3、 如果查询条件中 generic key 只出现某个或者某部分,则不会进行缓存操作。

4、 如果主键是只由一个字段组成,则不能设定为此种缓存。

5、 如果有 MANDT 字段,则为 generic key 的第一个字段(注:在创建表时, MANDT 只能是第一个字段,否则不起作用)

将相关的数据放入 Buffer ,至于将哪些数据放入,则根据设定的 generic key 为条件。

l Resident( 常驻 ) buffering ( 全部缓存 100%) The first time the table is accessed, its entire( 整个 ) contents are loaded in the table buffer. (在第一次读取表数据时,会将整个表的数据都会缓存下来,不管 WHERE 条件,都会将整个表缓存起来)

当从一个具有缓冲功能的表读取数据时,以下过程会发生:

1. ABAP program buffered table 中读取数据

2. ABAP processor 解析 Open SQL 语句。如果查询的表配置了绥中, ABAP processor 则会在服务器上检查 local buffer ,看查询的数据是否已缓存

3. 如果查询的数据没有被缓存,将会去查询数据库。如果存在,则将满足条件的数据返回到程序中。

4. 数据服务器会将数据传送到 SAP 应用服务器,并将它缓存下来

5. 将查找到的数据返回到应用程序

当修改一个具有缓冲功能的表时,以下动作会发生:

1. 数据库表与 SAP 应用服务器上的缓冲会被更新。数据库接口( database interface )记录修改日志到 table DDLOG 表中 (Buffer Synchronization ) 。如果系统不只有一个 application server ,其他的服务器上的缓存并不会立即更新。

2. 所有的应用服务器会定期的读取 table DDLOG ,根据该表中的日志来更新缓存。扫描的间隔与 buffering type 有关,在分布式系统中,同步时间为 60 seconds

3. 在未同步的应用服务上,用户读取的数据是旧的。该数据直到下次缓存同步时才认为过期。

适合使用缓存的表 :

1. 频繁读取的表

2. 很少修改的表

3. 相对小的表 (few lines, few columns, or short columns)

4. 延迟更新可接受的表

SELECT 语句在下列方式不会使用到缓存:

1. FROM 从句中使用了 BYPASSING BUFFER 选项

2. SELECT 从句中使用了 DISTINCT 选项

3. SELECT 从句使用 Aggregateexpressions

4. FROM 从句中使用了 join

5. WHERE 从句中使用了 IS NULL 条件

6. WHERE 从句中使用了 Subqueries (子查询)

7. 使用了 ORDER BY 从句 ( 如果排序的字段是主键,则还是会使用缓存 )

8. 使用了 GROUP BY 从句

9. 使用了 FOR UPDATE 选项 ( 经测试,好像还是会用到缓存,但 FOR UPDATE 是要依靠数据库系统本身来加锁的,但明确的指定绕过缓存选项又不支持: SELECT SINGLE FOR UPDATE… BYPASSING BUFFER… 这样的语句又不支持 )

10. 另外,所有的 Native SQL 语句不会使用到缓存

原文出自 江正军 技术博客,博客链接: www.cnblogs.com/jiangzhengjun