Recientemente tuve la tarea de enviar datos de los informes de SAP a la base de datos MS SQL, periódicamente, y también crear tablas de los informes requeridos en su base de datos MS SQL.
Siendo un desarrollador ABAP completamente novato, aprendí sobre Conectividad ABAP DB (ADBC) DDL y DML métodos, que me ayudaron a completar la tarea de forma más dinámica, en lugar de usar SQL EXEC con marcadores de posición? para cada campo
Antes de seguir adelante, me gustaría señalar lo obvio de que de ninguna manera mi enfoque es el mejor/óptimo que existe.
Entonces, comencemos el código con los siguientes objetivos:
REPORT ZSCHEDULE.
DATA:
CREATE_FIELDS TYPE STRING,
CREATE_PKEY TYPE STRING,
INSERT_VALUES TYPE STRING,
IT_INSERT TYPE STRINGTAB,
DATUM TYPE DATUM,
UZEIT TYPE UZEIT.
FIELD-SYMBOLS: <LT_SALV> TYPE ANY TABLE,
<VALUE> TYPE ANY.
DATA:
LS_VBAK TYPE VBAK,
LS_VBAP TYPE VBAP.
SELECT-OPTIONS:
SO_VKORG FOR LS_VBAK-VKORG,
SO_VTWEG FOR LS_VBAK-VTWEG,
SO_SPART FOR LS_VBAK-SPART,
SO_VSTEL FOR LS_VBAP-VSTEL,
SO_VKBUR FOR LS_VBAK-VKBUR.
START-OF-SELECTION.
TRY.
CL_SALV_BS_RUNTIME_INFO=>SET( DISPLAY = ABAP_FALSE
METADATA = ABAP_TRUE
DATA = ABAP_TRUE ).
SUBMIT Z_RPT_PROG
WITH SO_VKORG IN SO_VKORG
WITH SO_VTWEG IN SO_VTWEG
WITH SO_SPART IN SO_SPART
WITH SO_VSTEL IN SO_VSTEL
WITH SO_VKBUR IN SO_VKBUR
AND RETURN.
DATA(SALV_META) = CL_SALV_BS_RUNTIME_INFO=>GET_METADATA( ).
CL_SALV_BS_RUNTIME_INFO=>GET_DATA_REF( IMPORTING R_DATA = DATA(SALV_DATA) ).
ASSIGN SALV_DATA->* TO <LT_SALV>.
CL_SALV_BS_RUNTIME_INFO=>CLEAR_ALL( ).
...
He usado el formidable CL_SALV_BS_RUNTIME_INFO clase para extraer datos ALV del programa SUBMIT
También he cogido el Metadatos ALV porque necesito Catálogo de campo del programa SUBMIT para crear nuestros campos DB Table
Entonces tuve que escribir una lógica para crear diferentes campos de tipo de datos en SQL, para usar en CREAR MESA Sentencia SQL nativa dinámicamente
...
LOOP AT SALV_META-T_FCAT INTO DATA(WA_FCAT).
REPLACE ALL OCCURRENCES OF '/' IN WA_FCAT-FIELDNAME WITH '_'. " forward slash / is considered COMMENT in SQL
MODIFY SALV_META-T_FCAT FROM WA_FCAT TRANSPORTING FIELDNAME.
CASE WA_FCAT-DATATYPE.
WHEN 'CHAR'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } varchar({ WA_FCAT-INTLEN })|.
WHEN 'CUKY'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } varchar(5)|.
WHEN 'UNIT'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } varchar(3)|.
WHEN 'DATS'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } date|.
WHEN 'TIMS'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } time|.
WHEN 'RAW'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } nvarchar(max)|.
WHEN 'NUMC' OR 'INT4' OR 'DEC' OR 'QUAN' OR 'CURR'.
CREATE_FIELDS = CREATE_FIELDS && |{ WA_FCAT-FIELDNAME } float|.
ENDCASE.
CREATE_FIELDS = CREATE_FIELDS && ','.
IF WA_FCAT-KEY EQ 'X'.
IF CREATE_PKEY IS NOT INITIAL.
CREATE_PKEY = CREATE_PKEY && ','.
ENDIF.
CREATE_PKEY = CREATE_PKEY && |[{ WA_FCAT-FIELDNAME }]|.
ENDIF.
ENDLOOP.
DATA(TABLE) = `SAP_ZREPORT`.
IF CREATE_PKEY IS INITIAL.
DATA(CREATE_TABLE) = |CREATE TABLE { TABLE } ( { CREATE_FIELDS } )|.
ELSE.
CREATE_TABLE = |CREATE TABLE { TABLE } ( { CREATE_FIELDS } PRIMARY KEY ({ CREATE_PKEY }) )|.
ENDIF.
...
CREAR MESA La declaración SQL DDL está lista con todos los campos en el catálogo de campos, junto con las claves principales y lista para ejecutarse con ADBC EXECUTE_DDL declaración
Como estaba creando campos de tabla de base de datos SQL con nombres de campo de SAP, el cliente solicitó tener campo Descripción en la sección Comentarios de cada campo para su identificación, como el siguiente:
Así que usé ALTERAR TABLA Declaración SQL DDL para agregar Descripción en el campo Comentario
...
LOOP AT SALV_META-T_FCAT ASSIGNING FIELD-SYMBOL(<FIELD>).
CASE <FIELD>-DATATYPE.
WHEN 'CHAR'.
<FIELD>-TOOLTIP = |varchar({ <FIELD>-INTLEN })|.
WHEN 'CUKY'.
<FIELD>-TOOLTIP = |varchar(5)|.
WHEN 'UNIT'.
<FIELD>-TOOLTIP = |varchar(3)|.
WHEN 'DATS'.
<FIELD>-TOOLTIP = |date|.
WHEN 'TIMS'.
<FIELD>-TOOLTIP = |time|.
WHEN 'RAW'.
<FIELD>-TOOLTIP = |nvarchar(max)|.
WHEN 'NUMC' OR 'INT4' OR 'DEC' OR 'QUAN' OR 'CURR'.
<FIELD>-TOOLTIP = |float|.
ENDCASE.
REPLACE ALL OCCURRENCES OF '/' IN <FIELD>-SELTEXT WITH '-'. " forward slash / is considered COMMENT in SQL
IF <FIELD>-SELTEXT IS INITIAL. " COMMENT/DESCRIPTION cannot be blank in ALTER TABLE statement
<FIELD>-SELTEXT = <FIELD>-FIELDNAME.
ENDIF.
ENDLOOP.
LOOP AT SALV_META-T_FCAT INTO DATA(WA_TABLE_FIELDS).
DATA(ALTER_TABLE) = |ALTER TABLE { TABLE } ALTER COLUMN "{ WA_TABLE_FIELDS-FIELDNAME }" { WA_TABLE_FIELDS-TOOLTIP } NULL|.
DATA(ALTER) = |{ ALTER_TABLE } EXECUTE sp_addextendedproperty 'MS_Description', '{ WA_TABLE_FIELDS-SELTEXT }', 'Schema', 'dbo', 'table', '{ TABLE }', 'column', '{ WA_TABLE_FIELDS-FIELDNAME }';|.
APPEND ALTER TO IT_INSERT.
ENDLOOP.
...
Tenga en cuenta que ALTERAR TABLA Sentencia SQL DDL con MS_Descripción es solo para MS SQL y debe pasarse con el mismo orden como:
ALTER TABLE
ALTER COUMN “ ” NULL EJECUTAR sp_addextendedproperty ‘MS_Description’, ‘ ‘, ‘Schema’, ‘ ‘, ‘table’, ‘ ‘, ‘column’, ‘ ‘;
y he anexado ALTERAR declaraciones en la tabla interna IT_INSERT
ahora es el momento de la ejecución de SQL nativo declaraciones usando ADBC
DATA(DBCONN) = CL_SQL_CONNECTION=>GET_CONNECTION( 'SQL' ).
DATA(CREATE_STATEMENT) = DBCONN->CREATE_STATEMENT( ).
CREATE_STATEMENT->EXECUTE_DDL( CREATE_TABLE ). " create table statement
LOOP AT IT_INSERT INTO DATA(ROW).
CREATE_STATEMENT->EXECUTE_DDL( ROW ). " alter table statment
ENDLOOP.
DBCONN->COMMIT( ).
DBCONN->CLOSE( ).
Así que ahora mi tabla está creada en el Base de datos ODBCque se muestra en la captura de pantalla anterior, no necesito CREAR MESA y ALTERAR TABLA declaraciones porque fue un proceso de una sola vez, ya que voy a ejecutar este programa a tiempo para enviar datos a la base de datos remota
La parte complicada fue donde tuve que crear INSERTAR Declaración SQL DML con mis valores completos de estructura/área de trabajo, en consecuencia, usando bucle anidado y ASSIGN COMPONENT
y tuve que escribir lógica para manejar el campo FECHA y HORA para SQL porque SQL usa el formato ISO para FECHA y HORA
...
LOOP AT <LT_SALV> ASSIGNING FIELD-SYMBOL(<WA>).
LOOP AT SALV_META-T_FCAT ASSIGNING <FIELD>.
IF INSERT_VALUES IS NOT INITIAL.
INSERT_VALUES = INSERT_VALUES && ','.
ENDIF.
ASSIGN COMPONENT <FIELD>-FIELDNAME OF STRUCTURE <WA> TO <VALUE>.
IF <VALUE> IS ASSIGNED.
IF <FIELD>-DATATYPE = 'DATS'.
IF <VALUE> IS INITIAL.
INSERT_VALUES = INSERT_VALUES && |''|.
ELSE.
DATUM = <VALUE>.
DATA(DATE_STR) = |{ DATUM DATE = ISO }|.
INSERT_VALUES = INSERT_VALUES && |'{ DATE_STR }'|.
ENDIF.
ELSEIF <FIELD>-DATATYPE = 'TIMS'.
IF <VALUE> IS INITIAL.
INSERT_VALUES = INSERT_VALUES && |''|.
ELSE.
UZEIT = <VALUE>.
DATA(TIME_STR) = |{ UZEIT TIME = ISO }|.
INSERT_VALUES = INSERT_VALUES && |'{ TIME_STR }'|.
ENDIF.
ELSE.
INSERT_VALUES = INSERT_VALUES && |'{ <VALUE> }'|.
ENDIF.
ENDIF.
ENDLOOP.
DATA(INSERT) = |INSERT INTO { TABLE } VALUES ({ INSERT_VALUES })|.
APPEND INSERT TO IT_INSERT.
CLEAR: INSERT, INSERT_VALUES.
ENDLOOP.
IF IT_INSERT IS NOT INITIAL.
DATA(DBCONN) = CL_SQL_CONNECTION=>GET_CONNECTION( 'SQL' ).
DATA(INSERT_STATEMENT) = DBCONN->CREATE_STATEMENT( ).
LOOP AT IT_INSERT INTO DATA(ROW).
INSERT_STATEMENT->EXECUTE_UPDATE( ROW ).
ENDLOOP.
ENDIF.
DBCONN->COMMIT( ).
DBCONN->CLOSE( ).
IF SY-SUBRC = 0.
MESSAGE 'SQL operation successfull!' TYPE 'I' DISPLAY LIKE 'S'.
ENDIF.
CATCH CX_ROOT INTO DATA(CX_ERROR).
DATA(ERROR) = CX_ERROR->GET_LONGTEXT( ).
IF ERROR IS INITIAL.
ERROR = CX_ERROR->GET_TEXT( ).
ENDIF.
MESSAGE ERROR TYPE 'I'.
ENDTRY.
esto concluye el paso final y esta publicación de blog
Este código también se puede usar para informes estándar y CDS, sin ningún problema
me gustaría mencionar que ESTE BLOG lo que me ayudó, mucho, a escribir mi código
Me encantaría tener sus comentarios
Calle Eloy Gonzalo, 27
Madrid, Madrid.
Código Postal 28010
Paseo de la Reforma 26
Colonia Juárez, Cuauhtémoc
Ciudad de México 06600
Real Cariari
Autopista General Cañas,
San José, SJ 40104
Av. Jorge Basadre 349
San Isidro
Lima, LIM 15073