把SAP的表推送到SQL SERVER
- 配置数据库连接 (T-Code: DBCO):
登录 SAP,运行事务码 DBCO 或 DBA_COCKPIT。
点击"新条目"创建一个新连接(例如命名为 SQL_SERVER_DB)。
DBMS: 选择 MSS (代表 Microsoft SQL Server)。
用户名/密码: 输入拥有 SQL Server 读写权限的账号密码。
连接信息: 输入类似 MSSQL_SERVER=你的服务器IP MSSQL_DBNAME=你的数据库名。

推送类方法:
css
CLASS zcl_sql_server_gateway DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
" 构造函数:默认连接名 ZMSS
METHODS constructor
IMPORTING iv_dbcon_name TYPE dbcon-con_name DEFAULT 'ZMSS'
RAISING cx_sql_exception.
" 1. 动态查询 (Read)
METHODS execute_query
IMPORTING iv_sql TYPE string
EXPORTING et_result_tab TYPE ANY TABLE
RAISING cx_sql_exception.
" 2. 批量增量推送 (Upsert: Create/Update)
METHODS push_data_upsert
IMPORTING
iv_interface_id TYPE char32
iv_target_table TYPE string
it_key_fields TYPE string_table
it_data TYPE ANY TABLE
RETURNING
VALUE(rv_rows) TYPE i
RAISING
cx_sql_exception
cx_parameter_invalid.
" 3. 通用 DML 执行 (适用于简单的 DELETE/UPDATE 语句)
METHODS execute_dml
IMPORTING iv_sql TYPE string
RETURNING VALUE(rv_rows) TYPE i
RAISING cx_sql_exception.
" 4. 基于内表的批量删除 (Delete) - 极速模式
METHODS delete_by_itab
IMPORTING
iv_target_table TYPE string
it_key_fields TYPE string_table
it_data TYPE ANY TABLE
RETURNING
VALUE(rv_rows) TYPE i
RAISING
cx_sql_exception
cx_parameter_invalid.
" 5. 获取增量水位线
METHODS get_watermark
IMPORTING iv_interface_id TYPE char32
RETURNING VALUE(rv_tstmp) TYPE timestampl.
" 6. 关闭连接
METHODS close_connection
RAISING cx_sql_exception.
PRIVATE SECTION.
DATA mo_conn TYPE REF TO cl_sql_connection.
" 更新同步水位线
METHODS update_watermark
IMPORTING iv_interface_id TYPE char32.
ENDCLASS.
CLASS ZCL_SQL_SERVER_GATEWAY IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->CLOSE_CONNECTION
* +-------------------------------------------------------------------------------------------------+
* | [!CX!] CX_SQL_EXCEPTION
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD close_connection.
IF mo_conn IS BOUND.
mo_conn->close( ).
CLEAR mo_conn.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_DBCON_NAME TYPE DBCON-CON_NAME (default ='ZMSS')
* | [!CX!] CX_SQL_EXCEPTION
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD constructor.
mo_conn = cl_sql_connection=>get_connection( iv_dbcon_name ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->DELETE_BY_ITAB
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_TARGET_TABLE TYPE STRING
* | [--->] IT_KEY_FIELDS TYPE STRING_TABLE
* | [--->] IT_DATA TYPE ANY TABLE
* | [<-()] RV_ROWS TYPE I
* | [!CX!] CX_SQL_EXCEPTION
* | [!CX!] CX_PARAMETER_INVALID
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD delete_by_itab.
CHECK it_data IS NOT INITIAL.
DATA(lo_table_desc) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ).
DATA(lo_struct_desc) = CAST cl_abap_structdescr( lo_table_desc->get_table_line_type( ) ).
DATA(lt_fields) = lo_struct_desc->get_components( ).
DATA(lv_field_list) = REDUCE string( INIT s = `` FOR ls IN lt_fields
NEXT s = COND #( WHEN s IS INITIAL THEN |[{ ls-name }]| ELSE s && |, [{ ls-name }]| ) ).
DATA(lv_temp_table) = |#DEL_{ iv_target_table }|.
mo_conn->create_statement( )->execute_update( |SELECT TOP 0 { lv_field_list } INTO { lv_temp_table } FROM { iv_target_table }| ).
DATA(lv_marks) = REDUCE string( INIT m = `` FOR i = 1 UNTIL i > lines( lt_fields )
NEXT m = COND #( WHEN m IS INITIAL THEN `?` ELSE m && `, ?` ) ).
DATA(lo_ins_stmt) = mo_conn->create_statement( ).
lo_ins_stmt->set_param_table( table_ref = REF #( it_data ) ).
lo_ins_stmt->execute_update( |INSERT INTO { lv_temp_table } ({ lv_field_list }) VALUES ({ lv_marks })| ).
DATA(lv_where) = REDUCE string( INIT c = `` FOR k IN it_key_fields
NEXT c = COND #( WHEN c IS INITIAL THEN |T.[{ k }] = S.[{ k }]| ELSE |{ c } AND T.[{ k }] = S.[{ k }]| ) ).
" SQL Server 关联删除语法
DATA(lv_del_sql) = |DELETE T FROM { iv_target_table } AS T INNER JOIN { lv_temp_table } AS S ON { lv_where }|.
rv_rows = mo_conn->create_statement( )->execute_update( lv_del_sql ).
mo_conn->create_statement( )->execute_update( |DROP TABLE { lv_temp_table }| ).
mo_conn->commit( ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->EXECUTE_DML
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_SQL TYPE STRING
* | [<-()] RV_ROWS TYPE I
* | [!CX!] CX_SQL_EXCEPTION
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD execute_dml.
rv_rows = mo_conn->create_statement( )->execute_update( iv_sql ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->EXECUTE_QUERY
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_SQL TYPE STRING
* | [<---] ET_RESULT_TAB TYPE ANY TABLE
* | [!CX!] CX_SQL_EXCEPTION
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD execute_query.
DATA(lo_stmt) = mo_conn->create_statement( ).
DATA(lo_res) = lo_stmt->execute_query( iv_sql ).
" 注意:结果集对象使用 itab_ref
lo_res->set_param_table( itab_ref = REF #( et_result_tab ) ).
lo_res->next_package( ).
lo_res->close( ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->GET_WATERMARK
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_INTERFACE_ID TYPE CHAR32
* | [<-()] RV_TSTMP TYPE TIMESTAMPL
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_watermark.
SELECT SINGLE last_sync_tstmp FROM ztb_sync_log
WHERE interface_id = @iv_interface_id INTO @rv_tstmp.
IF sy-subrc <> 0.
rv_tstmp = '19700101000000'.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_SQL_SERVER_GATEWAY->PUSH_DATA_UPSERT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_INTERFACE_ID TYPE CHAR32
* | [--->] IV_TARGET_TABLE TYPE STRING
* | [--->] IT_KEY_FIELDS TYPE STRING_TABLE
* | [--->] IT_DATA TYPE ANY TABLE
* | [<-()] RV_ROWS TYPE I
* | [!CX!] CX_SQL_EXCEPTION
* | [!CX!] CX_PARAMETER_INVALID
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD push_data_upsert.
CHECK it_data IS NOT INITIAL.
" RTTI 解析结构
DATA(lo_table_desc) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ).
DATA(lo_struct_desc) = CAST cl_abap_structdescr( lo_table_desc->get_table_line_type( ) ).
DATA(lt_fields) = lo_struct_desc->get_components( ).
" 构建字段列表 CSV
DATA(lv_field_list) = REDUCE string( INIT s = `` FOR ls IN lt_fields
NEXT s = COND #( WHEN s IS INITIAL THEN |[{ ls-name }]| ELSE s && |, [{ ls-name }]| ) ).
" A. 创建会话级临时表
DATA(lv_temp_table) = |#TMP_{ iv_target_table }|.
mo_conn->create_statement( )->execute_update( |SELECT TOP 0 { lv_field_list } INTO { lv_temp_table } FROM { iv_target_table }| ).
" B. 批量插入到临时表
DATA(lv_marks) = REDUCE string( INIT m = `` FOR i = 1 UNTIL i > lines( lt_fields )
NEXT m = COND #( WHEN m IS INITIAL THEN `?` ELSE m && `, ?` ) ).
DATA(lo_insert_stmt) = mo_conn->create_statement( ).
" 注意:执行对象使用 table_ref
lo_insert_stmt->set_param_table( table_ref = REF #( it_data ) ).
lo_insert_stmt->execute_update( |INSERT INTO { lv_temp_table } ({ lv_field_list }) VALUES ({ lv_marks })| ).
" C. 构造 MERGE 逻辑 (处理主键排除)
DATA(lv_on_cond) = REDUCE string( INIT c = `` FOR k IN it_key_fields
NEXT c = COND #( WHEN c IS INITIAL THEN |T.[{ k }] = S.[{ k }]| ELSE |{ c } AND T.[{ k }] = S.[{ k }]| ) ).
DATA(lv_update_fields) = ``.
LOOP AT lt_fields ASSIGNING FIELD-SYMBOL(<ls_f>).
READ TABLE it_key_fields WITH KEY table_line = <ls_f>-name TRANSPORTING NO FIELDS.
IF sy-subrc <> 0.
lv_update_fields = COND #( WHEN lv_update_fields IS INITIAL THEN |T.[{ <ls_f>-name }] = S.[{ <ls_f>-name }]|
ELSE |{ lv_update_fields }, T.[{ <ls_f>-name }] = S.[{ <ls_f>-name }]| ).
ENDIF.
ENDLOOP.
DATA(lv_insert_vals) = REDUCE string( INIT v = `` FOR ls IN lt_fields
NEXT v = COND #( WHEN v IS INITIAL THEN |S.[{ ls-name }]| ELSE |{ v }, S.[{ ls-name }]| ) ).
" D. 执行 MERGE
DATA(lv_merge_sql) = |MERGE INTO { iv_target_table } AS T | &&
|USING { lv_temp_table } AS S ON ({ lv_on_cond }) | &&
|WHEN MATCHED THEN UPDATE SET { lv_update_fields } | &&
|WHEN NOT MATCHED THEN INSERT ({ lv_field_list }) VALUES ({ lv_insert_vals });|.
rv_rows = mo_conn->create_statement( )->execute_update( lv_merge_sql ).
mo_conn->create_statement( )->execute_update( |DROP TABLE { lv_temp_table }| ).
update_watermark( iv_interface_id ).
mo_conn->commit( ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_SQL_SERVER_GATEWAY->UPDATE_WATERMARK
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_INTERFACE_ID TYPE CHAR32
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD update_watermark.
GET TIME STAMP FIELD DATA(lv_now).
MODIFY ztb_sync_log FROM @( VALUE #( interface_id = iv_interface_id last_sync_tstmp = lv_now last_user = sy-uname ) ).
ENDMETHOD.
ENDCLASS.
推送表示例程序:
css
REPORT zfi_tables_post.
TABLES: zfit046,
zfit047,
zfit048.
SELECTION-SCREEN BEGIN OF BLOCK blk1 WITH FRAME TITLE TEXT-001.
PARAMETERS: p_gjahr TYPE gjahr OBLIGATORY DEFAULT sy-datum+0(4).
PARAMETERS: p_full TYPE char1 DEFAULT ' '.
SELECTION-SCREEN END OF BLOCK blk1.
INITIALIZATION.
IF sy-langu NE '1'.
PERFORM mod_sel_text USING sy-repid 'P' 'P_GJAHR' 'Fiscal Year'.
PERFORM mod_sel_text USING sy-repid 'P' 'P_FULL' 'Full Load(X=Full, blank=Current Year)'.
ENDIF.
START-OF-SELECTION.
PERFORM frm_push_zfit046.
PERFORM frm_push_zfit047.
PERFORM frm_push_zfit048.
*----------------------------------------------------------------------*
FORM frm_push_zfit046.
DATA: lo_gateway TYPE REF TO zcl_sql_server_gateway.
DATA: lt_zfit046 TYPE STANDARD TABLE OF zfit046.
DATA: lv_del_sql TYPE string.
IF p_full = 'X'.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE @lt_zfit046
FROM zfit046.
ELSE.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE @lt_zfit046
FROM zfit046
WHERE gjahr = @p_gjahr.
ENDIF.
IF lt_zfit046 IS INITIAL.
MESSAGE 'ZFIT046 no data!' TYPE 'S' DISPLAY LIKE 'W'.
RETURN.
ENDIF.
TRY.
lo_gateway = NEW zcl_sql_server_gateway( 'ZMSS' ).
IF p_full = 'X'.
lv_del_sql = |DELETE FROM ZFIT046|.
ELSE.
lv_del_sql = |DELETE FROM ZFIT046 WHERE GJAHR = '{ p_gjahr }'|.
ENDIF.
lo_gateway->execute_dml( lv_del_sql ).
lo_gateway->push_data_upsert(
iv_interface_id = 'INT_FI_BS'
iv_target_table = 'ZFIT046'
it_key_fields = VALUE string_table(
( `MANDT` )
( `BUKRS` )
( `GJAHR` )
( `ZSEQNO` )
)
it_data = lt_zfit046
).
MESSAGE |ZFIT046 pushed { lines( lt_zfit046 ) } rows successfully!| TYPE 'S'.
CATCH cx_root INTO DATA(lx_ex).
MESSAGE |ZFIT046 push failed: { lx_ex->get_text( ) }| TYPE 'E'.
ENDTRY.
ENDFORM.
*----------------------------------------------------------------------*
FORM frm_push_zfit047.
DATA: lo_gateway TYPE REF TO zcl_sql_server_gateway.
DATA: lt_zfit047 TYPE STANDARD TABLE OF zfit047.
DATA: lv_del_sql TYPE string.
IF p_full = 'X'.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE @lt_zfit047
FROM zfit047.
ELSE.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE @lt_zfit047
FROM zfit047
WHERE gjahr = @p_gjahr.
ENDIF.
IF lt_zfit047 IS INITIAL.
MESSAGE 'ZFIT047 no data!' TYPE 'S' DISPLAY LIKE 'W'.
RETURN.
ENDIF.
TRY.
lo_gateway = NEW zcl_sql_server_gateway( 'ZMSS' ).
IF p_full = 'X'.
lv_del_sql = |DELETE FROM ZFIT047|.
ELSE.
lv_del_sql = |DELETE FROM ZFIT047 WHERE GJAHR = '{ p_gjahr }'|.
ENDIF.
lo_gateway->execute_dml( lv_del_sql ).
lo_gateway->push_data_upsert(
iv_interface_id = 'INT_FI_IS'
iv_target_table = 'ZFIT047'
it_key_fields = VALUE string_table(
( `MANDT` )
( `BUKRS` )
( `GJAHR` )
( `ZSEQNO` )
)
it_data = lt_zfit047
).
MESSAGE |ZFIT047 pushed { lines( lt_zfit047 ) } rows successfully!| TYPE 'S'.
CATCH cx_root INTO DATA(lx_ex).
MESSAGE |ZFIT047 push failed: { lx_ex->get_text( ) }| TYPE 'E'.
ENDTRY.
ENDFORM.
*----------------------------------------------------------------------*
FORM frm_push_zfit048.
DATA: lo_gateway TYPE REF TO zcl_sql_server_gateway.
DATA: lt_zfit048 TYPE STANDARD TABLE OF zfit048.
DATA: lv_del_sql TYPE string.
IF p_full = 'X'.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE @lt_zfit048
FROM zfit048.
ELSE.
SELECT *
INTO CORRESPONDING FIELDS OF TABLE @lt_zfit048
FROM zfit048
WHERE gjahr = @p_gjahr.
ENDIF.
IF lt_zfit048 IS INITIAL.
MESSAGE 'ZFIT048 no data!' TYPE 'S' DISPLAY LIKE 'W'.
RETURN.
ENDIF.
TRY.
lo_gateway = NEW zcl_sql_server_gateway( 'ZMSS' ).
IF p_full = 'X'.
lv_del_sql = |DELETE FROM ZFIT048|.
ELSE.
lv_del_sql = |DELETE FROM ZFIT048 WHERE GJAHR = '{ p_gjahr }'|.
ENDIF.
lo_gateway->execute_dml( lv_del_sql ).
lo_gateway->push_data_upsert(
iv_interface_id = 'INT_FI_CF'
iv_target_table = 'ZFIT048'
it_key_fields = VALUE string_table(
( `MANDT` )
( `BUKRS` )
( `GJAHR` )
( `ZSEQNO` )
)
it_data = lt_zfit048
).
MESSAGE |ZFIT048 pushed { lines( lt_zfit048 ) } rows successfully!| TYPE 'S'.
CATCH cx_root INTO DATA(lx_ex).
MESSAGE |ZFIT048 push failed: { lx_ex->get_text( ) }| TYPE 'E'.
ENDTRY.
ENDFORM.
*----------------------------------------------------------------------*
FORM mod_sel_text USING p_repid kind parameter text.
DATA sel TYPE rsseltexts OCCURS 1 WITH HEADER LINE.
REFRESH sel.
sel-name = parameter.
sel-kind = kind.
sel-text = text.
APPEND sel.
CALL FUNCTION 'SELECTION_TEXTS_MODIFY'
EXPORTING
program = p_repid
TABLES
seltexts = sel
EXCEPTIONS
program_not_found = 1
program_cannot_be_generated = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
ENDFORM.
ALV推送示例:
css
IF sy-batch = 'X' AND sy-uname = 'JOB' .
PERFORM frm_push_zmmt058.
endif.
DATA: lo_gateway TYPE REF TO zcl_sql_server_gateway.
DATA: lv_del_sql TYPE string.
DATA: gt_out1 TYPE TABLE OF zmms058a_ss.
IF gt_out IS INITIAL.
MESSAGE 'No Data!' TYPE 'S' DISPLAY LIKE 'W'.
RETURN.
ENDIF.
MOVE-CORRESPONDING gt_out TO gt_out1.
TRY.
lo_gateway = NEW zcl_sql_server_gateway( 'ZMSS' ).
lv_del_sql = |DELETE FROM ZMMT058|.
lo_gateway->execute_dml( lv_del_sql ).
lo_gateway->push_data_upsert(
iv_interface_id = 'INT_MES_BS'
iv_target_table = 'ZMMT058'
it_key_fields = VALUE string_table(
( `WERKS` )
( `EBELN` )
( `EBELP` )
( `EBELN1` )
( `MATNR` )
)
it_data = gt_out1
).
MESSAGE |ZMMT058 pushed { lines( gt_out1 ) } rows successfully!| TYPE 'S'.
CATCH cx_root INTO DATA(lx_ex).
MESSAGE |ZMMT058 push failed: { lx_ex->get_text( ) }| TYPE 'E'.
ENDTRY.