原来的优化方案
问题:
- 对于字段总长超过512X5的表仍然读不了
- 数据量大时性能很差(分页查询效率很低)
解决方案
- 将每行记录的数据拆分为多行直接塞入到DATA表而不分到DATA1~DATA4中
并将每行记录占用的行数通过字段返回
客户端根据每行记录占用的data行数重新拼接data使用。 - RFC_READ_TABLE源码中,在SELECT. ENDSELECT.语句中拼接处理字段,遇到大表效率非常低
SELECT * FROM (QUERY_TABLE) INTO <WA> WHERE (OPTIONS).
IF SY-DBCNT GT ROWSKIPS.
* copy all relevant fields into DATA (output) table
LOOP AT FIELDS_INT.
IF FIELDS_INT-TYPE = 'P'.
ASSIGN COMPONENT FIELDS_INT-FIELDNAME
OF STRUCTURE <WA> TO <COMP>
TYPE FIELDS_INT-TYPE
DECIMALS FIELDS_INT-DECIMALS.
ELSE.
ASSIGN COMPONENT FIELDS_INT-FIELDNAME
OF STRUCTURE <WA> TO <COMP>
TYPE FIELDS_INT-TYPE.
ENDIF.
MOVE <COMP> TO
<D>+FIELDS_INT-OFFSET_DST(FIELDS_INT-LENGTH_DST).
ENDLOOP.
* end of loop at FIELDS_INT
APPEND DATA.
IF ROWCOUNT > 0 AND SY-DBCNT GE ROWCOUNT. EXIT. ENDIF.
ENDIF.
ENDSELECT.
- SAP 751后sql查询语法增加了offset关键字可以实现分页,参考官方文档
可以先分页查询出结果,再循环内表拼接字段,大幅提高效率。 - SAP 751之前版本没有offset关键字,则采用如下折中方法
- ROWSKIPS=0的请求,直接通过UP TO关键字控制查询结果行数,一次性查出记录
- ROWSKIPS>0的请求,通过SELECT. ENDSELECT.实现分页查询。
- 将1,2两步查询结果,通过循环内表处理拼接DATA。
优化后的源码全文
function zrfc_read_table .
*"----------------------------------------------------------------------
*"*"本地接口:
*" IMPORTING
*" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME
*" VALUE(DELIMITER) LIKE SONV-FLAG DEFAULT SPACE
*" VALUE(NO_DATA) LIKE SONV-FLAG DEFAULT SPACE
*" VALUE(ROWSKIPS) LIKE SOID-ACCNT DEFAULT 0
*" VALUE(ROWCOUNT) LIKE SOID-ACCNT DEFAULT 0
*" EXPORTING
*" VALUE(LINES_PER_RECORD) TYPE INTEGER
*" TABLES
*" OPTIONS STRUCTURE RFC_DB_OPT
*" FIELDS STRUCTURE RFC_DB_FLD
*" DATA STRUCTURE TAB512
*" EXCEPTIONS
*" TABLE_NOT_AVAILABLE
*" TABLE_WITHOUT_DATA
*" OPTION_NOT_VALID
*" FIELD_NOT_VALID
*" NOT_AUTHORIZED
*" DATA_BUFFER_EXCEEDED
*"----------------------------------------------------------------------
"
call function 'VIEW_AUTHORITY_CHECK'
exporting
view_action = 'S'
view_name = query_table
exceptions
no_authority = 2
no_clientindependent_authority = 2
no_linedependent_authority = 2
others = 1.
if sy-subrc = 2.
raise not_authorized.
elseif sy-subrc = 1.
raise table_not_available.
endif.
*
* find out about the structure of QUERY_TABLE
*
data begin of table_structure occurs 10.
include structure dfies.
data end of table_structure.
"DATA TABLE_HEADER LIKE X030L.
data table_type type dd02v-tabclass.
call function 'DDIF_FIELDINFO_GET'
exporting
tabname = query_table
* FIELDNAME = ' '
* LANGU = SY-LANGU
* LFIELDNAME = ' '
* ALL_TYPES = ' '
* GROUP_NAMES = ' '
importing
* X030L_WA =
ddobjtype = table_type
* DFIES_WA =
* LINES_DESCR =
tables
dfies_tab = table_structure
* FIXED_VALUES =
exceptions
not_found = 1
internal_error = 2
others = 3.
if sy-subrc <> 0.
raise table_not_available.
endif.
if table_type = 'INTTAB'.
raise table_without_data.
endif.
*
* isolate first field of DATA as output field
* (i.e. allow for changes to structure DATA!)
*
data line_length type i.
field-symbols <d>.
assign component 0 of structure data to <d>.
describe field <d> length line_length in character mode.
*
* if FIELDS are not specified, read all available fields
*
data number_of_fields type i.
describe table fields lines number_of_fields.
if number_of_fields = 0.
loop at table_structure.
move table_structure-fieldname to fields-fieldname.
append fields.
endloop.
endif.
*
* for each field which has to be read, copy structure information
* into tables FIELDS_INT (internal use) and FIELDS (output)
*
data: begin of fields_int occurs 10,
fieldname like table_structure-fieldname,
type like table_structure-inttype,
decimals like table_structure-decimals,
length_src like table_structure-intlen,
length_dst like table_structure-leng,
offset_src like table_structure-offset,
offset_dst like table_structure-offset,
end of fields_int,
line_cursor type i.
* 行数据缓存,此处按字段位置初始化分隔符
data record_buffer(30000).
line_cursor = 0.
* for each field which has to be read ...
loop at fields.
read table table_structure with key fieldname = fields-fieldname.
if sy-subrc ne 0.
raise field_not_valid.
endif.
* compute the place for field contents in DATA rows:
* if not first field in row, allow space for delimiter
if line_cursor <> 0.
if no_data eq space and delimiter ne space.
move delimiter to record_buffer+line_cursor.
endif.
line_cursor = line_cursor + strlen( delimiter ).
endif.
* ... copy structure information into tables FIELDS_INT
* (which is used internally during SELECT) ...
fields_int-fieldname = table_structure-fieldname.
fields_int-length_src = table_structure-intlen.
fields_int-length_dst = table_structure-leng.
fields_int-offset_src = table_structure-offset.
fields_int-offset_dst = line_cursor.
fields_int-type = table_structure-inttype.
fields_int-decimals = table_structure-decimals.
* compute the place for contents of next field in DATA rows
line_cursor = line_cursor + table_structure-leng.
* 超长记录拆分行,不再报缓冲溢出的异常
* if line_cursor > line_length and no_data eq space.
* raise data_buffer_exceeded.
* endif.
append fields_int.
* ... and into table FIELDS (which is output to the caller)
fields-fieldtext = table_structure-fieldtext.
fields-type = table_structure-inttype.
fields-length = fields_int-length_dst.
fields-offset = fields_int-offset_dst.
modify fields.
endloop.
* end of loop at FIELDS
* 每条记录需要数据行数
data: lc type f,
ll type f.
lc = line_cursor.
ll = line_length.
lines_per_record = ceil( lc / ll ).
*
* read data from the database and copy relevant portions into DATA
*
* output data only if NO_DATA equals space (otherwise the structure
* information in FIELDS is the only result of the module)
if no_data eq space.
* PSR
* 定义表ITAB结构
field-symbols <itab> type standard table.
data(comp_tab) = cast cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name(
query_table
)
)->get_components( ).
data(struct_descr) = cl_abap_structdescr=>create( comp_tab ).
data(table_descr) = cl_abap_tabledescr=>create( struct_descr ).
data table_ref type ref to data.
create data table_ref type handle table_descr.
assign table_ref->* to <itab>.
* 定义行记录W_ITAB结构
data: begin of work,
buffer(30000),
end of work.
field-symbols: <w_itab> type any, <comp> type any.
assign work to <w_itab> casting type (query_table).
* 准备select
data: select type table of edpline,
sublist type edpline.
loop at fields_int.
sublist = fields_int-fieldname && ','.
append sublist to select.
endloop.
assign select[ lines( select ) ] to field-symbol(<lastselect>).
replace `,` in <lastselect> with ``.
* 查询
if rowskips > 0.
if rowcount > 0.
rowcount = rowcount + rowskips.
endif.
select (select)
from (query_table)
into @<w_itab>
where (options)
order by primary key.
if sy-dbcnt gt rowskips.
append <w_itab> to <itab>.
if rowcount > 0 and sy-dbcnt ge rowcount. exit. endif.
endif.
endselect.
else.
select (select)
from (query_table)
into table @<itab>
* OFFSET @rowskips
up to @rowcount rows
where (options)
order by primary key.
endif.
* 处理返回数据
data data_offset type i.
loop at <itab> into <w_itab>.
loop at fields_int.
if fields_int-type = 'P'.
assign component fields_int-fieldname
of structure <w_itab> to <comp>
type fields_int-type
decimals fields_int-decimals.
else.
assign component fields_int-fieldname
of structure <w_itab> to <comp>
type fields_int-type.
endif.
move <comp> to record_buffer+fields_int-offset_dst(fields_int-length_dst).
endloop.
data_offset = 0.
do lines_per_record times.
move record_buffer+data_offset(line_length) to <d>.
append data.
data_offset = data_offset + line_length.
enddo.
endloop.
endif.
endfunction.