[SAP ABAP开发技术总结]OPEN SQL
OPEN SQL也是ABAP开发人员必备的知识,虽然可以使用原生的SQL来写,但不建议,就像JDBC与Hibernate的关系,性能与跨数据库的选择。但为了开发的简便与可移值性,还是使用Open SQL吧!这里贴出一些常用的操作,做项目时我也经常翻出来看一眼再写。
4. OPEN SQL
4.1. SELECT 、 INSERT 、 UPDATE 、 DELETE 、 MODIFY
如果从数据库读出来的数据存在重复时 , 不能存储到 Unique 内表中去 —— 如 Unique 的排序表与哈希表
SELECT SINGLE ... INTO [ CORRESPONDING FIELDS OF ] wa WHERE ...
SELECT SINGLE
<cols>
...
INTO
(
dobj1
,
dobj2
,
...
)
WHERE
...
SELECT ... FROM <tables> UP TO <n> ROWS ...
SELECT ... INTO | APPENDING CORRESPONDING FIELDS OF TABLE <itab> ...
单条插入 :在插入时是按照数据库表结构来解析 <wa> 结构,与 <wa> 中的字段名无关,所以 <wa> 的长度只少要等于或大于所对应表结构总长度
INSERT INTO <tabname> VALUES <wa>
INSERT <tabname> FROM <wa>
多条插入 : itab 内表的行结构也必须和数据库表的行结构一致; ACCEPTING DUPLICATE KEYS :如果现出关键字相同条目,系统将 SY-SUBRC 返回 4 ,并跳过该条目,但其他数据会插入进去
INSERT <tabname> FROM TABLE < itab > [ ACCEPTING DUPLICATE KEYS ]
单条更新: 会根据数据库表关键字来更新其他非关键字段。如果 WA 工作区是自己定义的且未参照数据库表,则 WA 的结构需要与数据库表相一致,且不能短于数据库表结构,但字段名可任意取
UPDATE dbtab FROM wa
多条更新: 主键不会被更新,即使在 SET 后面指定后也不会被更改
UPDATE dbtab SET f1 = g1 … fi = gi WHERE <conditions>
UPDATE dbtab FROM TABLE itab 与从 WA 工作区单条更新原理一样,根据数据表库关键字段来更新,且行结构要与数据库表结构一致,并且不能短于数据库表结构,一样内表行结构组件名可任意
单条删除: 下面的 WA 与 Itab 原理与 Update 是一样的
DELETE dbtab FROM wa
多条删除:
DELETE dbtab FROM TABLE itab
DELETE FROM dbtab WHERE <conditions>
插入或更新: 下面的 WA 与 Itab 原理与 Update 是一样的
MODIFY dbtab FROM wa 单行
MODIFY dbtab FROM TABLE itab 多行,有就修改,没有就插入
4.2. 条件操作符
= 、 <> 、 < 、 <= 、 > 、 >=
[
NOT
]
BETWEEN
...
AND
[
NOT
]
LIKE
[
NOT
]
IN
IS
[
NOT
]
NULL
4.3. RANG 条件内表
两种定义方式:
RANGES seltab FOR dobj [ OCCURS n]. 其中 dobj 为 自定义变量 或者是参照某个 表字段
SELECT-OPTIONS selcrit FOR {dobj|(name)}
上面两个语句会生成如下结构的内表,该条件内表的每一行都代表一个逻辑条件 :
DATA
:
BEGIN
OF
seltab
OCCURS
0
,
sign
TYPE
c
LENGTH
1
,
允许值为
I
和
E
,
I
表示包含
Include
,
E
表示排除
Exclude
option
TYPE
c
LENGTH
2
,
OPTION
表示选择运算符
,
low
LIKE
dobj
,
下界
,相当于
前面文本框
中的值
high
LIKE
dobj
,
上界
,相当于
后面文本框中的值
END
OF
rtab.
option : HIGH 字段为空,则取值可以为: EQ ( = )、 NE ( <> )、 GT ( > )、 GE ( >= )、 LE ( <= )、 LT ( < )、 CP 、 NP , CP (集合之内的数据)和 NP (集合之外数据)只有当在输入字段中使用了通配符(“ * ”或“ + ”)时它们才是有效的
SELECT ... WHERE ... field [NOT] IN seltab ...
如果 RANG 条件内表为空 , 则 IN seltab 逻辑表达试恒为真 , XX NOT IN seltab 恒为假
注: 不会像 FOR ALL ENTRIES 那样 , 忽略其他的条件表达式 , 其他条件还是起作用
4.4. FOR ALL ENTRIES
1、 使用该选项后,对于最后得出的结果集系统 会自动删除重复行 。因此如果你要保留重复行记录时,记得在 SELECT 语句中添加足够字段
2、 FOR ALL ENTRIES IN 后面使用的内部表 itab 如果 为空 ,将查出 当前 CLIENT 端 所有数据( 即忽略整个 WHERE 语句 ,其他条件都会被忽略)
3、 内表中的条件字段 不能使用 BETWEEN 、 LIKE 、 IN 比较操作符
4、 使用该语句时, ORDER BY 语句和 HAVING 语句 将 不能使用
5、 使用该语句时, 除 COUNT ( * ) (并且如果有了 COUNT 函数,则不能再选择其他字段,只能使用在 Select ... ENDSelect 语句中了)以外的 所有合计函数( MAX,MIN,AVG,SUM )都不能使用
即使 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 都会被忽略掉。
SELECT
matnr
FROM
mara
INTO
CORRESPONDING
FIELDS OF TABLE
strc
FOR ALL
ENTRIES
IN
strc
WHERE
matnr
=
strc
-
matnr
.
生成的 SQL 语句: SELECT "MATNR" FROM "MARA" WHERE "MANDT" = '210' AND "MATNR" IN ( '000000000000000101' , '000000000000000103' , '000000000000000104' )
注 :这里看上去 FOR ALL ENTRIES 使用 IN 表达式来代替了,这是只有使用到内表中一个条件是这样的,如果使用多个条件时,不会使用 In 表达式,而是使用 OR 连接,像这样:
另外,在使用 FOR ALL ENTRIES 时,不管使用了条件内表中的一个还是多个条件字段,都会以 5 个 值为单位进行 SQL 发送
4.5. INNER JOIN 、 LEFT OUTER JOIN 使用限制
ON 后面的条件与 Where 条件类似,但有以下不同:
2 必需有 ON 条件语句,且多个条件之间只能使用 AND 连接
2 每个条件表达式中 两个操作数之中必须有一个字段是来自于 JOIN 右表
2 如果是 LEFT OUTER JOIN ,则 至少有一个条件表达式的两个操作数一个是来自于左表,另一个来自右表
2 不能使用 NOT 、 LIKE 、 IN (但如果是 INNER JOIN ,则 > 、 < 、 BETWEEN …AND 、 <> 都可用)
2 如果是 LEFT OUTER JOIN ,则只能使用等号操作符: ( = 、 EQ )
2 如果是 LEFT OUTER JOIN , 同一右表不能多次出现在不同的 LEFT OUTER JOIN 的 ON 条件表达式中
2 LEFT OUTER JOIN 的右表所有字段不能出现在 WHERE 中
2 如果是 LEFT OUTER JOIN ,则在同一个 ON 条件语句中 只能与同一个左表进行关联
4.6. 动态 SQL
SELECT ( column_syntax ) FROM ...
column :可以是内表,也可以是字符串
TYPES
:
line_type
TYPE c
LENGTH
72
.
DATA
:
column_syntax
TYPE TABLE OF
line_type
.
APPEND
'CARRID'
TO
column_syntax
.
APPEND
'CITYFROM CITYTO'
TO
column_syntax
.
SELECT ... FROM (dbtab_syntax)...
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/OR
(
cond_syntax
) ...
DATA
:
cond
(
72
)
TYPE c
,
itab
LIKE TABLE OF
cond
.
APPEND
'cityfrom = ''NEW YORK'''
TO
itab
.
APPEND
'or cityfrom = ''SAN FRANCISCO'''
TO
itab
.
SELECT
*
INTO TABLE
itab_spfli
FROM
spfli
WHERE
(
itab
)
.
DATA
:
cond1
(
72
)
TYPE c VALUE
'cityfrom = ''NEW YORK'''
,
cond2
(
72
)
TYPE c VALUE
'cityfrom = ''SAN FRANCISCO'''
.
SELECT
*
INTO TABLE
itab_spfli
FROM
spfli
WHERE
(cond1)
OR
(cond2)
.
DATA
:
cond
(
72
)
TYPE c
,
cond1
(
72
)
TYPE c VALUE
'cityfrom = ''NEW YORK'''
,
itab
LIKE TABLE OF
cond
.
APPEND
'cityfrom = ''SAN FRANCISCO'''
TO
itab
.
SELECT
*
INTO TABLE
itab_spfli
FROM
spfli
WHERE
(itab)
OR
(cond1)
.
DATA
:
cond
(
72
)
TYPE c
,
itab
LIKE TABLE OF
cond
.
APPEND
'cityfrom = ''SAN FRANCISCO'''
TO
itab
.
SELECT
*
INTO TABLE
itab_spfli
FROM
spfli
WHERE
(itab)
OR
cityfrom
=
'NEW YORK'
4.7. 子查询
colum operator [ALL|ANY|SOME] 、 [NOT] EXISTS 、 [NOT] IN 连接至 WHERE 从句与 HAVING 从句中
4.7.1. = 、 <> 、 < 、 <= 、 > 、 >= 子查询
子查询的 SELECT 中 只有 一个表字段 或者是 一个统计列 ,并且 只能返回一条数据
SELECT
*
FROM
sflight
INTO
wa_sflight
WHERE
seatsocc
=
(
SELECT
MAX
( seatsocc )
FROM
sflight ).
ENDSELECT
.
操作符可以是: = 、 <> 、 < 、 <= 、 > 、 >=
4.7.1.1. ALL 、 ANY 、 SOME
如果子查询 返回的是多条 ,则使用 ALL 、 ANY 、 SOME 来修饰
SELECT
customid
COUNT
( * )
FROM
sbook
INTO
(id, cnt)
GROUP
BY
customid
HAVING
COUNT
( * )
>=
ALL
(
SELECT
COUNT
( * )
FROM
sbook
GROUP
BY
customid ).
ENDSELECT
.
2 ALL :主查询数据大于 所有 子查询返回的行数据时,才为真
2 ANY|SOME :主查询数据只要大于 任何 一条子查询返回的行数据时,才为真
2 = ANY|SOME :等效 IN 子查询
4.7.2. [NOT] IN 子查询
此类子查询 SELECT 中也只有单独的 一列 选择列,但查询出的 结果可能有多条
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 ).
4.7.3. [NOT] EXISTS 子查询
这类 子查询没有返回值 ,也 不要求 SELECT 从句中只有一个选择列 ,选择列可以任意个数, WHERE 或者 HAVING 从句根据该子查询的是否查询到数据来决定外层主查询语句来选择相应数据
SELECT
carrname
INTO
TABLE
name_tab
FROM
scarr
WHERE
EXISTS
(
SELECT
*
FROM
spfli
WHERE
carrid =
scarr~carrid
AND
cityfrom =
'NEW YORK'
).
4.7.4. 相关子查询
上面的示例子查询即为 相关子查询
如果某个子查的 WHERE 条件中引用了外层查询语句的列,则称此子查询为相关子查询。相关子查询对外层查询结果集中的每条记录都会执行一次,所以尽量少用相关子查询
4.8. 统计函数
MAX 、 MIN 、 AVG 、 SUM 、 COUNT ,聚合函数都可以加上 DISTINCT 选项
4.9. 分组过滤
如果将统计函数与 GROUP BY 子句一起使用,那么 Select 语句中未出现在统计函数的数据库字段都必须在 GROUP BY 子句中出现 。如果使用 INTO CORRESPONDING FIELDS 项,则需要在 Select 语句中通过 AS 后面的别名将统计结果存放到与之相应同名的内表字段中:
SELECT MIN
(
price
)
AS
m
INTO
price
FROM
sflight
GROUP BY
carrid
HAVING
MAX(
price
)
>
10
.
Having
从句中比较统计结果时,需要将统计函数重写一遍,而不能使用
Select
中定义的别名
ENDSELECT
.
4.10. 游标
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
.
4.11. 三种缓存
l 单记录缓存 :从数据库中仅读取一条数据并存储到 table buffer 中。此缓存只对 SELECT SINGLE… 语句起作用
l 部分缓存 :需要在指定 generic key (即关键字段组合,根据哪些关键字段来缓存,可以是部分或全部关键字段)。 如果主键是由一个字段构成,则不能选择此类型缓存 。当你使用 generic key 进行数据访问时,则属于此条件范围的整片数据都会被加载到 table buffer 中
1 、 查询时如果使用 BYPASSING BUFFER 选项,除了绕过缓存直接到数据库查询外,查出的数据不会放入缓存
2 、 只要查询条件中出现了用作缓存区域的所有关键字段,则查询出所有满足条件全部数据进行缓存
3 、 如果查询条件中 generic key 只出现某个或者某部分,则不会进行缓存操作
4 、 如果主键是只由一个字段组成,则不能设定为此种缓存
5 、 如果有 MANDT 字段,则为 generic key 的第一个字段
l 全部缓存 :在第一次读取表数据时,会将整个表的数据都会缓存下来,不管 WHERE 条件
4.12. Native SQL
4.12.1. 查询
DATA
:
BEGIN OF
wa
,
connid
TYPE
spfli
-
connid
,
cityfrom
TYPE
spfli
-
cityfrom
,
cityto
TYPE
spfli
-
cityto
,
END OF
wa
.
DATA
c1
TYPE
spfli
-
carrid
VALUE
'LH'
.
" Native SQL 语句不能以句点号结尾 ;
" 不能在 EXEC SQL…ENDEXEC 间有注释 ,即不能有星号与双引号的出现;
" 参数占位符使用冒号,而不是问号;
EXEC SQL PERFORMING
loop_output
.
SELECT
connid
,
cityfrom
,
cityto
INTO
:
wa
"
或使用:
INTO :wa-connid ,:wa-cityfrom ,:wa-cityto
FROM
spfli
WHERE
carrid
=
:
c1
ENDEXEC.
FORM
loop_output
.
WRITE
:
/ wa
-
connid
,
wa
-
cityfrom
,
wa
-
cityto
.
ENDFORM
4.12.2. 存储过程
EXEC SQL.
EXECUTE PROCEDURE
proc1
(
IN
:
x,
OUT
:
y,
INOUT
:
z
)
ENDEXEC
.
4.12.3. 游标
DATA
:
arg1
TYPE
string
VALUE
'800'
.
TABLES
:
t001
.
EXEC SQL
.
OPEN
c1
FOR
SELECT
MANDT
,
BUKRS
FROM
T001
"
打开游标
WHERE
MANDT
= :
arg1
AND
BUKRS >=
'ZA01'
ENDEXEC
.
DO
.
EXEC SQL
.
FETCH NEXT
c1
INTO
:
t001
-
mandt
, :
t001
-
bukrs
"
读取游标
ENDEXEC
.
IF
sy
-
subrc <>
0
.
EXIT
.
ELSE
.
WRITE
:
/ t001
-
mandt
,
t001
-
bukrs
.
ENDIF
.
ENDDO
.
EXEC SQL
.
CLOSE
c1
"
关闭游标
ENDEXEC
.
4.13. SAP 锁
通用数据库表锁函数: ENQUEUE_E_TABLE 、 DEQUEUE_E_TABLE 、 DEQUEUE_ALL (解锁所有)
特定数据库表锁函数: EN QUEUE _<LOCK OBJECT> 、 DENQUEUE _<LOCK OBJECT>
自定义的锁对象都必须以 EZ_ 或者 EY_ 开头来命名
|
允许第二次加锁模式 |
||
第一次加锁模式 |
S |
E |
X |
S 共享锁 |
是 ( 是 ) |
否( 是 ) |
否(否) |
E 可重入的排他锁 |
否( 是 ) |
否( 是 ) |
否(否) |
X 排他锁 |
否(否) |
否(否) |
否(否) |
括号内为同一程序(即同一事务内)内,括号外为非同一程序内
CALL FUNCTION
'ENQUEUE
_EZ_ZSPFLI'
"
加锁
EXPORTING
mode_zspfli
=
'E'
mandt
=
sy
-
mandt
carrid
=
'AA'
connid
=
'0011'
* X_CARRID
= ' '"
设置字段初始值(
Initial Value
),若为
X
,则当遇到与
CARRID
的初始值
Initial Value
相同值时才会设置锁对象。
CARRID
的初始值只需在数据库表字段中选择
Initial Value
选项
(SE11
中设置
)
。当没有设置
X
时,则会用该锁函数所设置的
Default Value
指定初始值
* X_CONNID
= ' '
* _SCOPE
= '2'"
该选项只有在
UPDATE
函数(
CALL FUNCTION FM IN UPDATE TASK
)中才起作用,用来控制锁的传递。一般当调用解锁函数
DEQUEUE
或程序结束时(
LEAVE PROGRAM
或者
LEAVE TO TRANSACTION
)锁就会被自动解除,另外,遇到
A
、
X
消息时或用户在命令框中输入
/n
时锁也会被解除,但是,当事务码正在执行
UPDATE
函数时就不一样了,函数结束时是否自动解除锁则要看该选项
_SCOPE
了:
1-
表示程序内有效
2-
表示
update module
内有效
3-
全部
* _WAIT
= ' '"
表示如果对象已经被锁定
,
是否等待后再尝试加锁
* _COLLECT
= ' '"
参数表示是否收集后进行统一提交
程序锁定: ENQUEUE_ ES_PROG 和 DEQUEUE_ES_PROG