[ABAP] NativeSQL: Zugriff auf eine Oracle-Datenbank

* DBCO: Transaktion zur Pflege der Datenbankverbindung zur Oracle-DB
* DBACOCKPIT
* DBCON: Tabelle der Datenbankverbindungen

* http://www.saptechnical.com/Tutorials/Basis/Oracle/RetrieveData.htm
* http://sqlines.com/oracle-to-sql-server/to_char_datetime
* https://archive.sap.com/discussions/thread/33542
* https://archive.sap.com/discussions/thread/107384
* https://blogs.sap.com/2012/12/28/native-sql-its-use-with-database-connection-in-sap/
* https://help.sap.com/http.svc/rc/abapdocu_750_index_htm/7.50/de-DE/abapexec_connection.htm

* Beispieldatentyp für verschiedene Oracle-Datentypen
TYPES: BEGIN OF ty_s_tableline,
         col_number     TYPE i,                      " NUMBER
         col_number10_2 TYPE p LENGTH 10 DECIMALS 2, " NUMBER (10,2)
         col_number5    TYPE p LENGTH 5 DECIMALS 0,  " NUMBER (5)
         col_char       TYPE c LENGTH 16,            " VARCHAR2 (16 Char)
         col_date_conv  TYPE c LENGTH 19,            " DATE ('YYYY-MM-DD HH24:MI:SS')
         col_date_conv2 TYPE c LENGTH 15,            " DATE ('YYYYMMDD HH24MISS')
       END OF ty_s_tableline.

TYPES: ty_it_table TYPE STANDARD TABLE OF ty_s_tableline WITH DEFAULT KEY.

* DB Connection zur Oracle DB
PARAMETERS: p_dbs TYPE dbcon-con_name DEFAULT 'MYDBCON'.
PARAMETERS: p_par1 TYPE matnr DEFAULT '12345'.

START-OF-SELECTION.

* Verbindungsinfos zur DB Connection lesen
  DATA: lv_dbtype TYPE dbcon_dbms.

  SELECT SINGLE dbms
    INTO @lv_dbtype
    FROM dbcon
    WHERE con_name = @p_dbs.

  IF sy-subrc = 0.
* Repräsentiert die Connection eine Oracle-DB?
    IF lv_dbtype = 'ORA'. " Domäne DBCON_DBMS

      TRY.
* Prüfen, ob die DB-Connection schon offen ist
          EXEC SQL.
            SET CONNECTION :p_dbs
          ENDEXEC.

          IF sy-subrc <> 0.
* Connect to DB
            EXEC SQL.
              CONNECT TO :p_dbs
            ENDEXEC.
          ENDIF.

          IF sy-subrc = 0.
* Session-Parameter setzen
            EXEC SQL.
              alter session set NLS_DATE_FORMAT = 'YYYYMMDD'
            ENDEXEC.

            EXEC SQL.
              alter session set NLS_NUMERIC_CHARACTERS = ',.'
            ENDEXEC.

* Open Cursor and SELECT
* SQL-Statements im ORACLE Syntax!
* Abbildung verschiedener Oracle-Datentypen als ABAP-Datentypen
* speziell das Datum (Orycle Typ DATE) muss Oracleseitig als Character-Feld (z.B. Länge 19 oder 15) interpretiert werden (CAST), damit es korrekt an SAP übertragen werden kann
* zusätzliche Komplexität durch INNER JOIN von zwei Tabellen mytab und mytab2
* Übergabe von Parametern an die EXEC SQL-Bereiche erfolgt immer durch ":Variablenname"
            EXEC SQL.
              OPEN dbcur FOR
              SELECT mytab.col_number,
                     mytab.col_number10_2,
                     mytab2.col_number5,
                     mytab.col_char,
                     CAST (TO_CHAR (mytab.col_date, 'YYYY-MM-DD HH24:MI:SS') AS VARCHAR2 (19)) AS col_date_conv
                     CAST (TO_CHAR (mytab.col_date, 'YYYYMMDD HH24MISS') AS VARCHAR2 (15)) AS col_date_conv2
              FROM myschemaname.mytablename mytab
              INNER JOIN myschemaname.mytablename2 mytab2 ON mytab.key1 = mytab2.key1 AND mytab.key2 = mytab2.key2
              WHERE mytab2.colxyz = :p_par1
            ENDEXEC.

            IF sy-subrc = 0.
              DATA: lv_line TYPE ty_s_tableline.
              DATA: it_table TYPE ty_it_table.

* Loop Cursor Data
              DO.
* DB-Cursor auf nächsten Datensatz setzen, solange, wie Daten vorhanden sind
                EXEC SQL.
                  FETCH NEXT dbcur INTO :lv_line
                ENDEXEC.
                IF sy-subrc = 0.
* Daten an itab anfügen
                  APPEND lv_line TO it_table.
                ELSE.
* keine Daten mehr vorhanden (EOF)
                  EXIT.
                ENDIF.
              ENDDO.

* Close Cursor
              EXEC SQL.
                CLOSE dbcur
              ENDEXEC.

            ENDIF.

* Reset auf "default connection"
            EXEC SQL.
              SET CONNECTION DEFAULT
            ENDEXEC.

* Disconnect
            EXEC SQL.
              DISCONNECT :p_dbs
            ENDEXEC.

* Daten ausgeben
            DATA: o_salv TYPE REF TO cl_salv_table.

            cl_salv_table=>factory( IMPORTING
                                      r_salv_table = o_salv
                                    CHANGING
                                      t_table      = it_table ).

            o_salv->get_functions( )->set_all( abap_true ).
            o_salv->get_columns( )->set_optimize( abap_true ).
            o_salv->get_display_settings( )->set_list_header( 'Oracle Data' ).
            o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
            o_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).

            LOOP AT o_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<c>).
              <c>-r_column->set_short_text( |{ <c>-r_column->get_columnname( ) }| ).
              <c>-r_column->set_medium_text( |{ <c>-r_column->get_columnname( ) }| ).
              <c>-r_column->set_long_text( |{ <c>-r_column->get_columnname( ) }| ).
            ENDLOOP.

            o_salv->display( ).
          ELSE.
            WRITE: / 'DB-Verbindung konnte nicht hergestellt werden.'.
          ENDIF.
        CATCH cx_root INTO DATA(e_txt).
          WRITE: / e_txt->get_text( ).
      ENDTRY.

    ELSE.
      WRITE: / |Falscher Datenbanktyp: { lv_dbtype }|.
    ENDIF.
  ELSE.
    WRITE: / |DB-Alias { p_dbs } nicht im System vorhanden.|.
  ENDIF.

[ABAP] OpenSQL: Verknüpfung von Tabelleninhalten über INNER JOIN, Zählung einzelner Datensatzgruppen (COUNT)

TYPES: BEGIN OF ty_ttxid,
         tdobject TYPE ttxid-tdobject,
         tdid     TYPE ttxid-tdid,
         tdtext   TYPE ttxit-tdtext,
         count    TYPE i,
       END OF ty_ttxid.
       
DATA: it_ttxid TYPE STANDARD TABLE OF ty_ttxid WITH DEFAULT KEY.

* count ist die Zählvariable für die Elemente der Gruppe, welche unter GROUP BY definiert wurde
* es werden nur Datensätze gezählt, die auch vorhanden sind, d.h. es gibt keine Werte mit count = 0
SELECT ttxid~tdobject, ttxid~tdid, ttxit~tdtext, COUNT( * ) AS count
  INTO CORRESPONDING FIELDS OF TABLE @it_ttxid
  FROM ttxid
  INNER JOIN ttxit ON ( ttxid~tdobject = ttxit~tdobject AND ttxid~tdid = ttxit~tdid )
  INNER JOIN stxh ON ( stxh~tdobject = ttxid~tdobject AND stxh~tdid = ttxid~tdid )
  WHERE ttxid~tdobject LIKE @p_obj
    AND ttxid~tdid LIKE @p_tdid
    AND stxh~tdfdate IN @so_date
    AND ttxit~tdspras = @p_lang
  GROUP BY ttxid~tdobject, ttxid~tdid, ttxit~tdtext.

[ABAP] OpenSQL: Tabelleninhalte zusammenfügen (mergen)

Variante 1 (ab NW 7.40 SP08)

* it_status hat zwei Spalten mit jeweils einer tiefen Struktur
SELECT jcds~*, tj02t~*
  FROM jcds INNER JOIN tj02t
  ON jcds~stat = tj02t~istat
  WHERE tj02t~spras = @sy-langu
  INTO TABLE @DATA(it_status)
  UP TO 100 ROWS.

IF sy-subrc = 0.
  cl_demo_output=>display( it_status ).
ENDIF.

Variante 2 (bis NW 7.40 SP08)

* it_status hat alle Spalten aus beiden includierten Strukturen
TYPES: BEGIN OF ty_data.
        INCLUDE TYPE jcds.
        INCLUDE TYPE tj02t.
TYPES: END OF ty_data.

DATA: it_status TYPE STANDARD TABLE OF ty_data WITH DEFAULT KEY.

SELECT jcds~*, tj02t~*
  FROM jcds INNER JOIN tj02t
  ON jcds~stat = tj02t~istat
  WHERE tj02t~spras = @sy-langu
  INTO TABLE @it_status
  UP TO 100 ROWS.

IF sy-subrc = 0.
  cl_demo_output=>display( it_status ).
ENDIF.

[ABAP] OpenSQL: Verknüpfte Abfrage mehrerer Tabellen, INNER JOIN, Alias

PARAMETERS: p_name TYPE string DEFAULT 'xyz'.

START-OF-SELECTION.

* SELECT durch Verknüpfung über mehrere Tabellen
  SELECT u~bname,
         u~persnumber,
         u~addrnumber,
         a~smtp_addr
    INTO TABLE @DATA(it_usr)
    FROM usr21 AS u
    INNER JOIN adr6 AS a ON a~persnumber = u~persnumber AND a~addrnumber = u~addrnumber
    WHERE u~bname = @name.

  cl_demo_output=>display( it_usr ).