1.開発システム。
2.テストシステム。
3.本稼働システム。
開発システムには通常最小限のデータしか含まれておらず、常に十分ではないため、プログラムの開発プロセスでは、生産システムまたはテストシステムでSQLクエリをすばやくテストすることが必要になることがよくあります。 これに使用できるオプションを見て、それらの欠点を評価し、最終的に独自のツールを開発しましょう。
私は5つの利用可能なオプションを数えることができました:
1.トランザクションSE16 / SE16N
このトランザクションでは、1つのテーブルのみから選択できます。 複数のテーブルを持つクエリには適していません。
2.トランザクションST04(追加機能-> SQLコマンドエディター)
このツールを使用すると、複雑なSQLクエリを実行できますが、2つの欠点があります。
- 第一に、ネイティブSQLクエリ(DBMS構文)のみを受け入れます。これにより、オープン性のためにABAPプログラムを開発するときに、構文がわずかに異なるOpen SQLクエリが使用されます。第二に「;
- 次に、OracleがDBMSとして使用されている場合にのみ機能します。
3.トランザクションSQVI
トランザクションでSQLクエリを直接記述することはできませんが、コンストラクターを使用して、JOINを持ついくつかのテーブルからかなり複雑な選択を構築できます。 サブクエリの操作方法がわからないうえ、デザイナーではマウス操作を多く実行する必要があるため、クエリのテストには適していません。
4.テストリクエストを使用して簡単なプログラムを作成し、テストシステムに転送する
変更されたコードをテスト(本稼働)システムに転送するプロセスには、いくつかの日常的な操作が必要で、平均5-7分かかります。したがって、このオプションは、リクエストを編集するたびにこれを行う忍耐がないため適切ではありません。
5. DBMSへの直接アクセス
ほとんどの場合、開発者がプロジェクトでそのようなアクセスを取得することはできないため、このオプションは適切ではありません。
おわりに
SAPの複雑なSQLクエリをすばやくテストできる便利な汎用ツールは存在しないことがわかりました。 この結論に達したので、私はそのようなツールを開発することにしました。
はじめに
まず、トランザクションSE80で、ZSQLプログラム、実行ボタン付きのMAIN100 GUIステータス、および画面0100を作成します。
拡張プログラムのアルゴリズムは次のようになります。
SQL SELECTの取得
SQLクエリを受け取るには、テキストエディターを使用します。テキストエディターは、 CL_GUI_TEXTEDITクラスを使用して画面上に作成します。 これを行うには、スクリーン0100にMYEDITという名前の空のコンテナーを追加し、そこにエディターを表示します。
画面上にテキストエディターを作成するコードスニペット
data: g_editor type ref to cl_gui_textedit, g_editor_container type ref to cl_gui_custom_container. if g_editor is initial. create object g_editor_container exporting container_name = `MYEDIT` exceptions cntl_error = 1 cntl_system_error = 2 create_error = 3 lifetime_error = 4 lifetime_dynpro_dynpro_link = 5. create object g_editor exporting parent = g_editor_container wordwrap_mode = cl_gui_textedit=>wordwrap_at_fixed_position wordwrap_to_linebreak_mode = cl_gui_textedit=>true exceptions others = 1. if sy-subrc <> 0. leave program. endif. endif.
SQLクエリの解析
入力したSQLクエリから、選択可能なフィールドとテーブルのリストを取得して、このリストに基づいてALV-Grid構造をさらに動的に生成し、結果を表示する必要があります。
コードスニペット分析リクエスト
types: ty_simple_tab type standard table of ty_simple_struc. types: ty_t_code type standard table of rssource-line. data lt_sql_query type ty_simple_tab. lt_fields type ty_simple_tab, lt_tables type ty_simple_tab, l_use_cnt(1) type c. " " loop at lt_sql_query assigning <fs_sql_query>. " - replace all occurrences of con_tab in <fs_sql_query>-line with space. concatenate ` ` <fs_sql_query>-line ` ` into <fs_sql_query>-line. " " refresh lt_parsed_sql_line. split <fs_sql_query>-line at space into table lt_parsed_sql_line. delete lt_parsed_sql_line where line = ''. loop at lt_parsed_sql_line assigning <fs_parsed_sql_line>. translate <fs_parsed_sql_line>-line to upper case. if <fs_parsed_sql_line>-line = 'SELECT'. continue. endif. " * - , " if <fs_parsed_sql_line>-line = '*'. l_field_names_obtained = 'X'. continue. endif. " FROM JOIN - , . " " if <fs_parsed_sql_line>-line = 'FROM' or <fs_parsed_sql_line>-line = 'JOIN'. l_field_names_obtained = 'X'. l_is_tabname = 'X'. continue. endif. " " if l_field_names_obtained is initial. " COUNT()" find 'COUNT(' in <fs_parsed_sql_line>-line ignoring case. if sy-subrc = 0. l_use_cnt = 'X'. continue. endif. " ~" search <fs_parsed_sql_line>-line for '~'. if sy-subrc = 0. add 1 to sy-fdpos. endif. append <fs_parsed_sql_line>-line+sy-fdpos to lt_fields. endif. " " if l_is_tabname = 'X'. append <fs_parsed_sql_line>-line to lt_tables. clear l_is_tabname. endif. endloop. endloop.
SQLクエリの実行
クエリを満たすために、 サブルーチンプール生成演算子を使用します。これにより、パラメータとして渡されたソースコードに基づいて一時的なABAPプログラムを動的に生成でき、入力されたSQLクエリから準備します。
ABAPプログラムを生成するコードスニペット
types: ty_t_code type standard table of rssource-line. data: code type ty_t_code, prog(8) type c, msg(120) type c, lt_parsed_sql_line type ty_simple_tab, l_sub_order(1) type c. field-symbols: <fs_sql_query> type ty_simple_struc, <fs_parsed_sql_line> type ty_simple_struc. append `program z_sql.` to code. append `form get_data using fs_data type standard table.` to code. append `try.` to code. loop at lt_sql_query assigning <fs_sql_query>. clear: lt_parsed_sql_line. split <fs_sql_query>-line at space into table lt_parsed_sql_line. delete lt_parsed_sql_line where line = ''. loop at lt_parsed_sql_line assigning <fs_parsed_sql_line>. concatenate ` ` <fs_parsed_sql_line>-line ` ` into <fs_parsed_sql_line>-line. translate <fs_parsed_sql_line>-line to upper case. " into… 1 , " if <fs_parsed_sql_line>-line = ' FROM ' and l_sub_order is initial. append `into corresponding fields of table fs_data` to code. l_sub_order = 'X'. endif. append <fs_parsed_sql_line>-line to code. endloop. endloop. append `.` to code. append `rollback work.` to code. append `catch cx_root.` to code. append `rollback work.` to code. append `message ``- , `` type ``i``.` to code. append `endtry.` to code. append `endform.` to code. generate subroutine pool code name prog message msg.
画面に結果を表示する
フィールドの構成とタイプは事前に不明であるため、結果を取得して画面に表示するには、リクエストで選択されたフィールドに基づいて内部テーブルとALV-Grid構造を動的に生成する必要があります。 これを行うには、 cl_alv_table_createクラスのcreate_dynamic_tableメソッドを使用します。
ALV-Grid構造を生成するコードスニペット
data: ref_table_descr type ref to cl_abap_structdescr, lt_tab_struct type abap_compdescr_tab, ls_fieldcatalog type slis_fieldcat_alv. field-symbols: <fs_tab_struct> type abap_compdescr, <fs_tables> type ty_simple_struc, <fs_fields> type ty_simple_struc. loop at lt_tables assigning <fs_tables>. refresh lt_tab_struct. " " ref_table_descr ?= cl_abap_typedescr=>describe_by_name( <fs_tables>-line ). lt_tab_struct[] = ref_table_descr->components[]. loop at lt_tab_struct assigning <fs_tab_struct>. " SQL- - " if lines( lt_fields ) > 0. read table lt_fields transporting no fields with key line = <fs_tab_struct>-name. if sy-subrc <> 0. continue. endif. endif. " , " read table lt_fieldcatalog transporting no fields with key fieldname = <fs_tab_struct>-name. if sy-subrc = 0. continue. endif. clear ls_fieldcatalog. ls_fieldcatalog-fieldname = <fs_tab_struct>-name. ls_fieldcatalog-ref_tabname = <fs_tables>-line. append ls_fieldcatalog to lt_fieldcatalog. endloop. endloop. " COUNT() – CNT INT" if l_use_cnt = 'X'. clear ls_fieldcatalog. ls_fieldcatalog-fieldname = 'CNT'. ls_fieldcatalog-seltext_l = '-'. ls_fieldcatalog-seltext_m = '-'. ls_fieldcatalog-seltext_s = '-'. ls_fieldcatalog-datatype = 'INT4'. if p_tech_names = 'X'. ls_fieldcatalog-seltext_l = 'CNT'. ls_fieldcatalog-seltext_m = 'CNT'. ls_fieldcatalog-seltext_s = 'CNT'. ls_fieldcatalog-reptext_ddic = 'CNT'. endif. append ls_fieldcatalog to lt_fieldcatalog. endif.
動的テーブルを作成するコードスニペット
data: dyn_table type ref to data, dyn_line type ref to data, lt_lvc_fieldcatalog type lvc_t_fcat, ls_lvc_fieldcatalog type lvc_s_fcat. field-symbols: <fs_fieldcatalog> type slis_fieldcat_alv. " " loop at lt_fieldcatalog assigning <fs_fieldcatalog>. clear ls_lvc_fieldcatalog. move-corresponding <fs_fieldcatalog> to ls_lvc_fieldcatalog. ls_lvc_fieldcatalog-ref_table = <fs_fieldcatalog>-ref_tabname. append ls_lvc_fieldcatalog to lt_lvc_fieldcatalog. endloop. " " call method cl_alv_table_create=>create_dynamic_table exporting it_fieldcatalog = lt_lvc_fieldcatalog importing ep_table = dyn_table. assign dyn_table->* to <fs_data>. create data dyn_line like line of <fs_data>. assign dyn_line->* to <fs_wa_data>.
ZSQLのソースコードの完全なリスト:
開示する
type-pools: slis. types: begin of ty_simple_struc, line(255) type c, end of ty_simple_struc. types: ty_simple_tab type standard table of ty_simple_struc. types: ty_t_code type standard table of rssource-line. data: g_editor type ref to cl_gui_textedit, g_editor_container type ref to cl_gui_custom_container, g_ok_code like sy-ucomm, p_tech_names(1) type c. field-symbols: <fs_data> type standard table, <fs_wa_data> type any. call screen 100. module pbo output. set pf-status `MAIN100`. " SQL-" if g_editor is initial. create object g_editor_container exporting container_name = `MYEDIT` exceptions cntl_error = 1 cntl_system_error = 2 create_error = 3 lifetime_error = 4 lifetime_dynpro_dynpro_link = 5. create object g_editor exporting parent = g_editor_container wordwrap_mode = cl_gui_textedit=>wordwrap_at_fixed_position wordwrap_to_linebreak_mode = cl_gui_textedit=>true exceptions others = 1. if sy-subrc <> 0. leave program. endif. endif. endmodule. module pai input. case sy-ucomm. when `EXIT`. leave program. when `EXEC`. " «» perform exec. endcase. endmodule. form exec. " " data lt_sql_query type ty_simple_tab. clear lt_sql_query. call method g_editor->get_text_as_r3table importing table = lt_sql_query exceptions others = 1. delete lt_sql_query where line = ''. " " data: lt_fields type ty_simple_tab, lt_tables type ty_simple_tab, l_use_cnt(1) type c. clear: lt_fields, lt_tables, l_use_cnt. perform parse_sql_query using lt_sql_query changing lt_fields lt_tables l_use_cnt. " ABAP- SQL-" data: code type ty_t_code, prog(8) type c, msg(120) type c. clear: code, prog, msg. perform create_get_function using lt_sql_query changing code. generate subroutine pool code name prog message msg. if sy-subrc <> 0. message msg type 'I'. return. endif. " ALV-Grid " data: lt_fieldcatalog type slis_t_fieldcat_alv. refresh: lt_fieldcatalog. perform get_fieldcat using lt_tables lt_fields p_tech_names l_use_cnt changing lt_fieldcatalog. " , , <fs_data>, " " perform create_itab_dynamically using lt_fieldcatalog. " SQL-, " perform get_data in program (prog) using <fs_data>. " " perform show_alv using lt_fieldcatalog. endform. " " form parse_sql_query using lt_sql_query type ty_simple_tab changing lt_fields type ty_simple_tab lt_tables type ty_simple_tab l_use_cnt. data: l_field_names_obtained(1) type c, l_is_tabname(1) type c, lt_parsed_sql_line type ty_simple_tab. clear: l_field_names_obtained, l_is_tabname. field-symbols: <fs_sql_query> type ty_simple_struc, <fs_parsed_sql_line> type ty_simple_struc. constants: con_tab type c value cl_abap_char_utilities=>horizontal_tab. " " loop at lt_sql_query assigning <fs_sql_query>. " - replace all occurrences of con_tab in <fs_sql_query>-line with space. concatenate ` ` <fs_sql_query>-line ` ` into <fs_sql_query>-line. " " refresh lt_parsed_sql_line. split <fs_sql_query>-line at space into table lt_parsed_sql_line. delete lt_parsed_sql_line where line = ''. loop at lt_parsed_sql_line assigning <fs_parsed_sql_line>. translate <fs_parsed_sql_line>-line to upper case. if <fs_parsed_sql_line>-line = 'SELECT'. continue. endif. " * - , " if <fs_parsed_sql_line>-line = '*'. l_field_names_obtained = 'X'. continue. endif. " FROM JOIN - , . " " if <fs_parsed_sql_line>-line = 'FROM' or <fs_parsed_sql_line>-line = 'JOIN'. l_field_names_obtained = 'X'. l_is_tabname = 'X'. continue. endif. " " if l_field_names_obtained is initial. " COUNT()" find 'COUNT(' in <fs_parsed_sql_line>-line ignoring case. if sy-subrc = 0. l_use_cnt = 'X'. continue. endif. " ~" search <fs_parsed_sql_line>-line for '~'. if sy-subrc = 0. add 1 to sy-fdpos. endif. append <fs_parsed_sql_line>-line+sy-fdpos to lt_fields. endif. " " if l_is_tabname = 'X'. append <fs_parsed_sql_line>-line to lt_tables. clear l_is_tabname. endif. endloop. endloop. endform. " ABAP- " form create_get_function using lt_sql_query type ty_simple_tab changing code type ty_t_code. data: lt_parsed_sql_line type ty_simple_tab, l_sub_order(1) type c. clear l_sub_order. field-symbols: <fs_sql_query> type ty_simple_struc, <fs_parsed_sql_line> type ty_simple_struc. append `program z_sql.` to code. append `form get_data using fs_data type standard table.` to code. append `try.` to code. loop at lt_sql_query assigning <fs_sql_query>. clear: lt_parsed_sql_line. split <fs_sql_query>-line at space into table lt_parsed_sql_line. delete lt_parsed_sql_line where line = ''. loop at lt_parsed_sql_line assigning <fs_parsed_sql_line>. concatenate ` ` <fs_parsed_sql_line>-line ` ` into <fs_parsed_sql_line>-line. translate <fs_parsed_sql_line>-line to upper case. " into… 1 , " if <fs_parsed_sql_line>-line = ' FROM ' and l_sub_order is initial. append `into corresponding fields of table fs_data` to code. l_sub_order = 'X'. endif. append <fs_parsed_sql_line>-line to code. endloop. endloop. append `.` to code. append `rollback work.` to code. append `catch cx_root.` to code. append `rollback work.` to code. append `message ``- , `` type ``i``.` to code. append `endtry.` to code. append `endform.` to code. endform. " ALV-" form get_fieldcat using lt_tables type ty_simple_tab lt_fields type ty_simple_tab p_tech_names l_use_cnt changing lt_fieldcatalog type slis_t_fieldcat_alv. data: ref_table_descr type ref to cl_abap_structdescr, lt_tab_struct type abap_compdescr_tab, ls_fieldcatalog type slis_fieldcat_alv. field-symbols: <fs_tab_struct> type abap_compdescr, <fs_tables> type ty_simple_struc, <fs_fields> type ty_simple_struc. loop at lt_tables assigning <fs_tables>. refresh lt_tab_struct. " " ref_table_descr ?= cl_abap_typedescr=>describe_by_name( <fs_tables>-line ). lt_tab_struct[] = ref_table_descr->components[]. loop at lt_tab_struct assigning <fs_tab_struct>. " SQL- - " if lines( lt_fields ) > 0. read table lt_fields transporting no fields with key line = <fs_tab_struct>-name. if sy-subrc <> 0. continue. endif. endif. " , " read table lt_fieldcatalog transporting no fields with key fieldname = <fs_tab_struct>-name. if sy-subrc = 0. continue. endif. clear ls_fieldcatalog. ls_fieldcatalog-fieldname = <fs_tab_struct>-name. ls_fieldcatalog-ref_tabname = <fs_tables>-line. append ls_fieldcatalog to lt_fieldcatalog. endloop. endloop. " COUNT() – CNT INT" if l_use_cnt = 'X'. clear ls_fieldcatalog. ls_fieldcatalog-fieldname = 'CNT'. ls_fieldcatalog-seltext_l = '-'. ls_fieldcatalog-seltext_m = '-'. ls_fieldcatalog-seltext_s = '-'. ls_fieldcatalog-datatype = 'INT4'. if p_tech_names = 'X'. ls_fieldcatalog-seltext_l = 'CNT'. ls_fieldcatalog-seltext_m = 'CNT'. ls_fieldcatalog-seltext_s = 'CNT'. ls_fieldcatalog-reptext_ddic = 'CNT'. endif. append ls_fieldcatalog to lt_fieldcatalog. endif. endform. " " form create_itab_dynamically using lt_fieldcatalog type slis_t_fieldcat_alv. data: dyn_table type ref to data, dyn_line type ref to data, lt_lvc_fieldcatalog type lvc_t_fcat, ls_lvc_fieldcatalog type lvc_s_fcat. field-symbols: <fs_fieldcatalog> type slis_fieldcat_alv. " " loop at lt_fieldcatalog assigning <fs_fieldcatalog>. clear ls_lvc_fieldcatalog. move-corresponding <fs_fieldcatalog> to ls_lvc_fieldcatalog. ls_lvc_fieldcatalog-ref_table = <fs_fieldcatalog>-ref_tabname. append ls_lvc_fieldcatalog to lt_lvc_fieldcatalog. endloop. " " call method cl_alv_table_create=>create_dynamic_table exporting it_fieldcatalog = lt_lvc_fieldcatalog importing ep_table = dyn_table. assign dyn_table->* to <fs_data>. create data dyn_line like line of <fs_data>. assign dyn_line->* to <fs_wa_data>. endform. " ALV-Grid " form show_alv using lt_fieldcatalog type slis_t_fieldcat_alv. data: ls_event type slis_alv_event, lt_event type slis_t_event, ls_layout type slis_layout_alv, l_repid like sy-repid. ls_layout-colwidth_optimize = 'X'. l_repid = sy-repid. call function 'REUSE_ALV_GRID_DISPLAY' exporting i_callback_program = l_repid is_layout = ls_layout it_fieldcat = lt_fieldcatalog i_save = 'X' tables t_outtab = <fs_data> exceptions program_error = 1 others = 2. if sy-subrc <> 0. leave program. endif. endform.
開発したプログラムを使用すると、任意の複雑なOpen SQL SELECTクエリを実行できます。 クエリの作成時に1つのルールを遵守する必要があります。COUNT()構造が使用されている場合、ALV-Gridが正しく生成されるように「AS cnt」を追加する必要があります。
理論的には、プログラムはわずかに変更でき、クエリのテストだけでなく、カスタムレポートの生成にも使用できます。
この記事では、セキュリティの問題には対処しませんでした。 受信したリクエストは、ABAPコードを記述して実行されると、正確性についてはまったくチェックされません。 この可能性を排除するには、単純なチェックを追加するだけで十分です。
完全なソース : https : //github.com/RusZ/SAP-SQL-Executor