SQL的子查询五花八门,有在SELECT里面的,有FROM里面的,有在WHERE里面的,有用IN实现的,有用EXISTS实现的等等,但是ABAP OPEN
SQL只支持EXISTS、NOT
EXISTS子查询。在很多时候,巧妙使用这两个子查询不但能够大幅度提高程序性能,还可以精简代码,增加代码的可读性,非常的哇塞!
令人痛心的是,在有些项目上,就像毫无道理的限制JOIN表个数一样,EXISTS也会引起很多开发者的误解,认为子查询是不好的,是性能低下的,从而禁止使用,这是多么的令人遗憾啊!本篇就是用两个例子来演示合适的子查询是多么的神奇。
例一
在PP开发中,订单的状态如技术完成、关闭、删除等是经常用来做数据筛选使用的,这个状态在生产订单界面是以字符串显示的,如下图:
假设有这么一个需求,需要拿到一个期间内状态包含TECO的生产订单。
初学者 一般会使用函数STATUS_TEXT_EDIT取出生产订单的状态文本,判断文本是否包含TECO。
熟练一些的则会想到直接在JEST取数然后判断,因为取状态文本实在是太慢了。
再熟练一些的ABAPer则会使用EXISTS来做,下面的代码分别展示了这三种方法:
*&---------------------------------------------------------------------**& Baitianzhen*& 使用EXISTS提高程序性能*&---------------------------------------------------------------------*REPORT ztest NO STANDARD PAGE HEADING.
DATA: BEGIN OF itab OCCURS 0, aufnr TYPE aufk-aufnr, erdat TYPE aufk-erdat, ernam TYPE aufk-ernam, objnr TYPE aufk-objnr, END OF itab.DATA: BEGIN OF it_jest OCCURS 0, objnr TYPE jest-objnr, END OF it_jest.DATA: ltext TYPE bsvx-sttxt.DATA: lines TYPE i.DATA: t1 TYPE i, t2 TYPE i.
SELECT-OPTIONS s_erdat FOR itab-erdat NO-EXTENSION.
START-OF-SELECTION. "方法一 GET RUN TIME FIELD t1. SELECT aufnr erdat ernam objnr INTO CORRESPONDING FIELDS OF TABLE itab FROM aufk WHERE erdat IN s_erdat. LOOP AT itab. CALL FUNCTION 'STATUS_TEXT_EDIT' EXPORTING objnr = itab-objnr spras = sy-langu bypass_buffer = 'X' IMPORTING line = ltext EXCEPTIONS object_not_found = 1 OTHERS = 2. IF ltext NS 'TECO'. DELETE itab. CONTINUE. ENDIF. ENDLOOP. GET RUN TIME FIELD t2. t1 = t2 - t1. lines = lines( itab ). WRITE: /(20)'STATUS_TEXT_EDIT:' ,lines,t1.
"方法二 CLEAR itab[]. GET RUN TIME FIELD t1. SELECT aufnr erdat ernam objnr INTO CORRESPONDING FIELDS OF TABLE itab FROM aufk WHERE erdat IN s_erdat. SELECT objnr INTO TABLE it_jest FROM jest FOR ALL ENTRIES IN itab WHERE objnr = itab-objnr AND stat = 'I0045' AND inact = ''. SORT it_jest. LOOP AT itab. READ TABLE it_jest WITH KEY objnr = itab-objnr BINARY SEARCH. IF sy-subrc <> 0. DELETE itab. CONTINUE. ENDIF. ENDLOOP. GET RUN TIME FIELD t2. t1 = t2 - t1. lines = lines( itab ). WRITE: /(20)'SELECT JEST再删除:',lines,t1.
"方法三 CLEAR itab[]. GET RUN TIME FIELD t1. SELECT aufnr erdat ernam objnr INTO CORRESPONDING FIELDS OF TABLE itab FROM aufk WHERE erdat IN s_erdat AND EXISTS ( SELECT * FROM jest WHERE objnr = aufk~objnr AND stat = 'I0045' AND inact = '' ). GET RUN TIME FIELD t2. t1 = t2 - t1. lines = lines( itab ). WRITE: /(20)'使用EXISTS:',lines,t1.下面我们来看执行结果,最后一列为执行时间(微秒),为了排除Buffer的影响,这个是连续执行几次的结果:
第一种方法大概用时15秒,第二种方法大概是1秒多,而用EXISTS方法只有0.068秒,220倍,爽不?
例二
在取物料凭证的时候,很多时候需要排除掉冲销凭证和已经被冲销的凭证,如果MSEG-SMBLN不为空说明此凭证为冲销凭证,SMBLN就是被冲销的凭证号。
下面为两种写法的代码:
*&---------------------------------------------------------------------**& Baitianzhen*& 使用NOT EXISTS提高程序性能*&---------------------------------------------------------------------*REPORT ztest NO STANDARD PAGE HEADING.
TABLES mkpf.DATA: BEGIN OF itab OCCURS 0, mblnr TYPE mseg-mblnr, mjahr TYPE mseg-mjahr, zeile TYPE mseg-zeile, END OF itab.DATA: BEGIN OF it_smbln OCCURS 0, sjahr TYPE mseg-sjahr, smbln TYPE mseg-smbln, smblp TYPE mseg-smblp, END OF it_smbln.DATA: lines TYPE i.DATA: t1 TYPE i, t2 TYPE i. SELECT-OPTIONS s_budat FOR mkpf-budat NO-EXTENSION.
START-OF-SELECTION. "方法一 GET RUN TIME FIELD t1. SELECT mblnr mjahr zeile INTO TABLE itab FROM mseg WHERE budat_mkpf IN s_budat. IF sy-subrc = 0. SELECT sjahr smbln smblp INTO TABLE it_smbln FROM mseg FOR ALL ENTRIES IN itab WHERE sjahr = itab-mjahr AND smbln = itab-mblnr AND smblp = itab-zeile. SORT it_smbln BY sjahr smbln smblp. LOOP AT itab. READ TABLE it_smbln WITH KEY sjahr = itab-mjahr smbln = itab-mblnr smblp = itab-zeile BINARY SEARCH. IF sy-subrc = 0. DELETE itab. CONTINUE. ENDIF. ENDLOOP. ENDIF. GET RUN TIME FIELD t2. t1 = t2 - t1. lines = lines( itab ). WRITE: /(20)'一般写法:',lines,t1.
"方法二 CLEAR itab[]. GET RUN TIME FIELD t1. SELECT mblnr mjahr zeile INTO TABLE itab FROM mseg WHERE budat_mkpf IN s_budat AND NOT EXISTS ( SELECT * FROM m_mbmps WHERE m_mbmps~sjahr = mseg~mjahr AND m_mbmps~smbln = mseg~mblnr AND m_mbmps~smblp = mseg~zeile ). GET RUN TIME FIELD t2. t1 = t2 - t1. lines = lines( itab ). WRITE: /(20)'使用EXISTS:',lines,t1.执行结果如 下:
由 26\. 85秒 变成 3.79秒,大概提 升7 倍 ,咋样,还行吧?
当然,就像其他取数一样,使用EXISTS、NOT EXISTS也是需要WHERE字段有索引的,否则也很慢。这是需要注意的一个地方。