Open SQL详解

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4293497.html

读取数据SELECT. 262

读取单行... 263

DISTINCT. 263

读取多行... 264

列别名... 264

存储到指定变量中... 264

SELECT * INTO….. 265

追加读取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的一部分,可以用它来创建与管理数据库表

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

Open SQL contains the following keywords:


Keyword


Function


SELECT


读取


INSERT


插入


UPDATE


修改


MODIFY


插入或修改


DELETE


删除


OPENCURSOR,FETCH,CLOSE CURSOR


游标


COMMIT WORK, ROLLBACK WORK

SY-SUBRC所有Open SQL在执行成功时返回0,否则返回非0

SY-DBCNT在执行Open SQL语句后,返回受影响的数据条数

读取数据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] BETWEENdobj1 AND dobj2}
| {col [NOT] LIKEdobj [ESCAPE esc]}
| {col [NOT] IN(dobj1, dobj2 ...)}
| {col [NOT] INseltab}
| {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].


sy-subrc


Meaning


0


查询到数据


4


没有查询到数据。聚合函数规则特殊,请参见


8


如果有WHERE从句后没有完全指定主键字段条件,FOR UPDATE时

sy-dbcnt 为查询到的记录条数

如果从数据库读出来的数据存在重复时,是不能存储到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 waFROM spfliWHERE 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 (<f1>, <f2>, ...). ...

此情况下要求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 [NOTBETWEEN dobj1 AND dobj2 ...

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

LIKE

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

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

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

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

以“EDIT_”开头的字符串

IN

SELECT ... WHERE <s>[NOT] IN (<f1>, ......, <fn>) ...

... 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>...

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

NOT

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

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

NULL

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>条件语句中不能使用LIKEBETWEENIN这些操作符,只能使用比较操作符

该选项的作用与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"

FROM

"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"

FROM

"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"

FROM

"MARA"

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

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

SELECT

"MATNR"

FROM

"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


id


size


1


10


2


20


3


30

表2:tab2


size


name


10


AAA


20


BBB


20


CCC

两条SQL:
1、select * form tab1 left join tab2 on (tab1.size = tab2.size) where tab2.name=’AAA’
2、select * form tab1 left join tab2 on (tab1.size = tab2.size and tab2.name=’AAA’)


第一条SQL的过程:


1、中间表on条件: 
tab1.size = tab2.size


tab1.id


tab1.size


tab2.size


tab2.name


1


10


10


AAA


2


20


20


BBB


2


20


20


CCC


3


30


(null)


(null)

   

2、再对中间表过滤where条件:tab2.name=’AAA’


tab1.id


tab1.size


tab2.size


tab2.name


1


10


10


AAA

   

第二条SQL的过程:


1、中间表on条件: 
tab1.size = tab2.size and tab2.name=’AAA’
(条件不为真也会返回左表中的记录)


tab1.id


tab1.size


tab2.size


tab2.name


1


10


10


AAA


2


20


(null)


(null)


3


30


(null)


(null)

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

子查询

使用子查询时会绕过SAP buffering

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

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

l  [NOTEXISTS ( SELECT …FROM … )

l  [NOTIN ( 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] <s1> ) [AS <a1>]<agg>( [DISTINCT] <s2> ) [AS <a2>] ...

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><s1> [AS <a1>] <s2> [AS <a2>] ...

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

...

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

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(但没有问题的数据行还是会被更新)。SY-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条件)

删除单行

DELETEdbtab 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-DBCNTcontains 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 dbtabFROM 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 语句不会使用到缓存

时间: 2024-10-17 04:21:16

Open SQL详解的相关文章

MyBatis的动态SQL详解

MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,本文详解mybatis的动态sql,需要的朋友可以参考下 MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力.如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号.动态 SQL 可以彻底处理这种痛苦. 通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种

Oracle中动态SQL详解(EXECUTE IMMEDIATE)

Oracle中动态SQL详解(EXECUTE IMMEDIATE) 2017年05月02日 18:35:48 悠悠倾我心 阅读数:744 标签: oracle动态sqloracle 更多 个人分类: 数据库 Oracle中动态SQL详解 1.静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情况属于这种类型:另外一种是后期联编(late binding),即SQL语句只有在运

sql详解

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中.它是D.RichardHipp建立的公有领域项目.它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了.它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl.C#.PHP.Java等,还有ODBC接口,同样比起Mysql.PostgreSQL这两款开源的世界著名数据库

MyBatis 动态sql详解

MyBatis的动态sql语句 1.if 条件 2.choose , when 和 otherwise条件 3.where 条件 where条件:1.自动加上where.2.如果where子句以and或者or开头,则自动删除第一个and或者or..所以我们不需要自己加where 4.trim 条件 trim条件和where条件类似但是功能更强大:不仅可以替换掉子句开头的and或者or,还提供了加前缀和后缀的功能. 5.forEach循环 6.set 条件 set条件:自动加上set,自动去除最后

Oracle中动态SQL详解

1.静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情况属于这种类型:另外一种是后期联编(late binding),即SQL语句只有在运行阶段才能建立,例如当查询条件为用户输入时,那么Oracle的SQL引擎就无法在编译期对该程序语句进行确定,只能在用户输入一定的查询条件后才能提交给SQL引擎进行处理.通常,静态SQL采用前一种编译方式,而动态SQL采用后一种编译方式.

MyBatis动态SQL详解

MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑. MyBatis中用于实现动态SQL的元素主要有: if choose(when,otherwise) trim where set foreach if就是简单的条件判断,利用if语句我们可以实现某些简单的条件选择.先来看如下一个例子: <select id="dynamicIfTest" parameterType="Blog" resultType=&quo

(转)Oracle中动态SQL详解

本文转载自:http://www.cnblogs.com/gaolonglong/archive/2011/05/31/2064790.html 1.静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情况属于这种类型:另外一种是后期联编(late binding),即SQL语句只有在运行阶段才能建立,例如当查询条件为用户输入时,那么Oracle的SQL引擎就无法在编译期对该程

动态SQL详解

动态SQL 在之前用户所编写的PL/SQL程序时有一个最大的特点:就是所操作的数据库对象(例如:表)必须存在,否则创建的子程序就会出问题,而这样的操作在开发之中被称为静态SQL操作,而动态SQL操作可以让用户在定义程序时不指定具体的操作对象,而在执行时动态的传入所需要的数据库对象,从而使程序变得更加的灵活. 创建一个功能,输入表名,输出表中有多少行,若没有这个表,则创建这个表. 首先禁用所有触发器 GRANT CREATE ANY TABLE TO SCOTT create or replace

MyBatis探究-----动态SQL详解

1.if标签 接口中方法:public List<Employee> getEmpsByEmpProperties(Employee employee); XML中:where 1=1必不可少 <select id="getEmpsByEmpProperties" resultType="com.mybatis.entity.Employee"> select * from t_employee where 1=1 <if test=&