Sentencias SQL
Leer y grabar en la base de datos con Open SQL y trabajar con tablas internas.
- Manual ABAP
- 6 de 14
- Anterior
- Pantalla de selección
- Siguiente
- Entrada y salida de datos
Antes y ahora
Este capítulo se escribió para ABAP clásico. Las ideas siguen valiendo, pero casi todas las sentencias se escriben hoy de otra forma. Este es el resumen; en cada apartado tienes la comparación con código y, plegado, el texto original.
| Concepto | En el manual | Hoy | Estado |
|---|---|---|---|
| Nombre | Open SQL | ABAP SQL | Se escribe distinto |
| Variables en la consulta | WHERE erdat >= lv_desde | WHERE erdat >= @lv_desde | Se escribe distinto |
| Lista de campos | Separados por espacios | Separados por comas; INTO al final | Se escribe distinto |
| Tabla de destino | Declarada antes con DATA … OCCURS | Declarada en la consulta: INTO TABLE @DATA(lt_pedidos) | Se escribe distinto |
| Leer y escribir a la vez | SELECT … ENDSELECT con WRITE dentro | Leer todo con INTO TABLE y mostrarlo con ALV | Sustituido |
| Datos de varias tablas | FOR ALL ENTRIES o SELECT anidados | JOIN o una vista CDS | Se escribe distinto |
| Recorrer tablas internas | Cabecera implícita: LOOP AT itab. | Área de trabajo o field-symbol: LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>) | Sustituido |
| Leer una línea | READ TABLE … WITH KEY … BINARY SEARCH | Expresión de tabla itab[ campo = valor ] y tablas SORTED o HASHED | Se escribe distinto |
| Contar líneas | DESCRIBE TABLE itab LINES n | lines( itab ) | Se escribe distinto |
| Vaciar una tabla | REFRESH itab | CLEAR itab | Sustituido |
| Subtotales | AT NEW / AT END OF con SUM | LOOP AT … GROUP BY y REDUCE | Se escribe distinto |
Sigue igual: COMMIT WORK y ROLLBACK WORK, las cláusulas WHERE, GROUP BY y ORDER BY, SORT, DELETE ADJACENT DUPLICATES, LOOP AT … WHERE.
6.1. Sentencias OPEN SQL
Se escribe distinto
Así se hace hoy
El SELECT se escribe con comas, las variables de ABAP llevan @ y el destino va al final. En lugar de leer registro a registro con SELECT … ENDSELECT, se lee todo de una vez en una tabla interna, que es mucho más rápido en HANA.
TABLES vbak.
SELECT * FROM vbak
WHERE erdat >= lv_desde
AND vkorg = p_vkorg.
WRITE: / vbak-vbeln, vbak-netwr.
ENDSELECT.SELECT FROM vbak
FIELDS vbeln, kunnr, netwr, waerk
WHERE erdat >= @lv_desde
AND vkorg = @p_vkorg
INTO TABLE @DATA(lt_pedidos).
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = lt_pedidos ).
lo_alv->display( ).Los cálculos, los CASE y las uniones se hacen ahora en la propia consulta, y la base de datos devuelve el resultado ya preparado:
SELECT vbeln kunnr netwr
FROM vbak
INTO CORRESPONDING FIELDS OF TABLE lt_pedidos
WHERE erdat >= lv_desde.
IF lt_pedidos[] IS NOT INITIAL.
SELECT vbeln posnr matnr
FROM vbap
INTO TABLE lt_pos
FOR ALL ENTRIES IN lt_pedidos
WHERE vbeln = lt_pedidos-vbeln.
ENDIF.SELECT FROM vbak AS c
INNER JOIN vbap AS p ON p~vbeln = c~vbeln
FIELDS c~vbeln, c~kunnr, p~posnr, p~matnr,
CASE c~auart WHEN 'TA' THEN 'Normal'
ELSE 'Otro' END AS tipo
WHERE c~erdat >= @lv_desde
ORDER BY c~vbeln, p~posnr
INTO TABLE @DATA(lt_pedidos).- Pide solo los campos que necesitas:
SELECT *en tablas anchas como MARA o VBAK desperdicia memoria y red. - Con
FOR ALL ENTRIES, comprueba siempre que la tabla de entrada no está vacía: si lo está, se ignora elWHEREy se lee toda la tabla. - Si una consulta se repite en varios programas, conviértela en una vista CDS y léela con
SELECTcomo si fuera una tabla.
Texto original del manual: sintaxis clásica
Se llaman sentencias Open SQL a aquellas propias de ABAP que simulan un subconjunto de sentencias SQL (“Structured Query Language”). En general, las sentencias SQL (del tipo que sean) se usan para el acceso a datos en tablas. Las Open SQL tratan tablas de bases de datos (es decir, las tablas del Diccionario de Datos) mientras que las Native SQL son sentencias propias de cada base de datos, que se escriben entre EXEC SQL y ENDEXEC).
Las sentencias Open SQL son: SELECT (lectura de registros de una tabla), INSERT (inserción de un registro en una tabla), UPDATE (modificación de un registro), OPEN CURSOR (abre un cursor), FETCH (avanza una posición el cursor), CLOSE CURSOR (cierra un cursor), COMMIT WORK (actualiza los últimos cambios de la BD, confirmando que se hacen las modificaciones), ROLLBACK WORK (deshace las modificaciones hechas a la BD volviendo al último checkpoint).
6.1.1. Sentencia SELECT
- SELECT [ SINGLE ]campos FROM tabla[ INTO … | APPENDING … ] [ WHERE …][ GROUP BY campos][ ORDER BY criterio].…[ ENDSELECT ].
Con SELECT se leen registros de una tabla del Diccionario de Datos (no vale para leer de una tabla interna). La tabla se indica tras FROM. Se pueden especificar los campos a leer (si se desean leer todos los campos de la tabla, poner un ‘*’ como ‘campos’) y si se trata de una lectura individual o múltiple (con SINGLE sólo se lee el primer registro que cumpla los criterios WHERE de búsqueda, acabando el SELECT. En este caso no se debe cerrar éste con ENDSELECT. Y sin SINGLE se leen todas las entradas que concuerden con dichos criterios). Devuelve SY-SUBRC 0 si encuentra algo (y en SY-DBCNT el nº de entradas leídas); y si no, 4. Parámetros:
- [ SINGLE [ FOR UPDATE ]][DISTINCT] { * | campos | MAX(c) AS m | MIN(c) AS m | AVG(c) AS m | SUM(c) AS m | COUNT(c) AS m | COUNT DISTINCT(c) AS m | COUNT(*) AS m }: Con DISTINCT se excluyen automáticamente las líneas duplicadas. Se puede especificar, en lugar de campos a leer, un parámetro ‘AS’ para que el SELECT simplemente calcule un valor numérico, como el máximo, mínimo, media, suma, … de todas las entradas (numéricas) encontradas.
- FROM tabla | vista | (tabla) [ CLIENT SPECIFIED ] [ BYPASSING BUFFER ] [ UP TO n ROWS ]: Este ‘parámetro’ es obligatorio. Especifica la tabla de la base de datos a leer. La tabla o vista debe existir en el D.D. No hace falta declararla con TABLES si se lee con INTO. Con (tabla) puede especificarse la tabla o vista dinámicamente (aquí ‘tabla’ es una variable). El mandante se especifica con CLIENT SPECIFIED. Con BYPASSING BUFFER se lee directamente de la base de datos sin pasar por el buffer de SAP. Se usa UP TO n ROWS para leer hasta una línea determinada (si n = 0, se leen todas; si n < 0, es un error).
- APPENDING[ CORRESPONDING FIELDS OF ] TABLE tabla[ PACKAGE SIZE n] Añade (INTO machaca) directamente los registros leídos en la tabla interna especificada.
- INTO[ CORRESPONDING FIELDS OF ] { field-string | TABLE tabla } [ PACKAGE SIZE n ]: Se usa para indicar al sistema el lugar donde debe dejar el dato leído (en una tabla interna o en un registro), y si guarda todos los campos o bien sólo los iguales en nombre. De esta forma no hace falta moverlos con un MOVE de la tabla de la BD a la tabla interna, basta hacer directamente el APPEND o COLLECT.
- WHERE campo_BD operador campo: En las cláusulas WHERE se definen los criterios de selección. Serán seleccionadas todas las entradas (si existen) de la tabla que cumplan estos criterios (o sólo la primera si es un SELECT SINGLE). Sin WHERE se lee toda la tabla. Primero se escribe siempre el campo de la tabla de la BD (sólo el nombre del campo, sin el nombre de la tabla) y luego el campo o variable con el que se compara. Los operadores pueden ser: EQ, NE, LT, LE, GT, GE, =, <> o bien ><, <, <=, >, >=. Hay otros operadores especiales: [ NOT ] BETWEEN c1 AND c2, [ NOT ] LIKE c [ ESCAPE e ], [ NOT ] IN (c1, …, cn), [ NOT ] IN criterio_selección,IS [ NOT ] NULL (tabla_condiciones).
- GROUP BY campo1 … campon (tabla): Esta opción permite combinar el contenido de un grupo de entradas de una tabla de la BD, en una única línea. Los campos a agrupar pueden especificarse de forma estática (en tiempo de compilación) o dinámica (en ejecución).
- ORDER BY[ PRIMARY KEY ] campo1 [ ASCENDING | DESCENDING ] … campon [ ASCENDING | DESCENDING ](tabla): Este parámetro sirve para definir el orden de tratamiento (criterio de ordenación) de las líneas seleccionadas por el SELECT. Con PRIMARY KEY la tabla se ordena por la clave primaria. Se puede ordenar por varios campos, de forma ascendente (por defecto) o descendente. También puede especificarse el criterio de ordenación a través de una tabla (definida con entradas de 72 caracteres máximo. En cada línea de la tabla se especifica un criterio de ordenación).
Ejemplo:
ABAP SELECT * FROM name WHERE campo1 = 'valor1' AND campo2 = 'valor2'. WRITE: / name-campo1, name-campo2. ENDSELECT.- [ SINGLE [ FOR UPDATE ]][DISTINCT] { * | campos | MAX(c) AS m | MIN(c) AS m | AVG(c) AS m | SUM(c) AS m | COUNT(c) AS m | COUNT DISTINCT(c) AS m | COUNT(*) AS m }: Con DISTINCT se excluyen automáticamente las líneas duplicadas. Se puede especificar, en lugar de campos a leer, un parámetro ‘AS’ para que el SELECT simplemente calcule un valor numérico, como el máximo, mínimo, media, suma, … de todas las entradas (numéricas) encontradas.
- SELECT [ SINGLE ]campos FROM tabla[ INTO … | APPENDING … ] [ WHERE …][ GROUP BY campos][ ORDER BY criterio].…[ ENDSELECT ].
6.1.2. Sentencia INSERT
Se escribe distinto
Así se hace hoy
INSERT, UPDATE, MODIFY y DELETE siguen existiendo; lo único que cambia es que las variables y tablas de ABAP llevan @:
INSERT zventas FROM wa_venta.
INSERT zventas FROM TABLE it_ventas
ACCEPTING DUPLICATE KEYS.
UPDATE zventas SET importe = lv_importe
WHERE cliente = lv_cliente.INSERT zventas FROM @ls_venta.
INSERT zventas FROM TABLE @lt_ventas
ACCEPTING DUPLICATE KEYS.
UPDATE zventas SET importe = @lv_importe
WHERE cliente = @lv_cliente.Escribe solo en tablas propias. Las tablas estándar de SAP (VBAK, MARA…) se actualizan con BAPIs o, en S/4HANA, con los objetos RAP y EML: así se aplican todas las validaciones. En ABAP Cloud no se permite escribir directamente en tablas de SAP.
Texto original del manual: sintaxis clásica
- INSERT [ INTO ] { tabla | (c) } [ CLIENT SPECIFIED ] [ VALUES field-string | FROM field-string ] | FROM TABLE tabla_interna[ ACCEPTING DUPLICATE KEYS ] ].
- Con INSERT se añaden nuevas entradas a una tabla del Diccionario de Datos (para una tabla interna se haría con APPEND o COLLECT). Dicha tabla puede especificarse de forma directa, con ‘tabla’, o indirecta, con ‘(c)’. En ambos casos ésta debe existir en el D.D. Con FROM TABLE inserta en una sola operación todas las entradas de la tabla_interna. Con CLIENT SPECIFIED se pueden insertar registros en la base de datos sobre otro mandante. El área de trabajo especificada en el field-string de VALUES debe ser tan ancha como el área de trabajo de la tabla de la base de datos. Con la opción ACCEPTING DUPLICATE KEYS, si una línea no puede ser insertada no se aborta la sentencia, aunque sigue devolviendo SY-SUBRC = 4.
6.2. Sentencias de tablas internas
Se escribe distinto
Así se hace hoy
Las sentencias de tablas internas siguen ahí, pero sin cabecera: se trabaja con un área de trabajo o un field-symbol, y muchas operaciones tienen ya una función o una expresión equivalente.
LOOP AT it_pedidos.
it_pedidos-netwr = it_pedidos-netwr * 2.
MODIFY it_pedidos.
ENDLOOP.
DESCRIBE TABLE it_pedidos LINES lv_n.
REFRESH it_pedidos.LOOP AT lt_pedidos ASSIGNING FIELD-SYMBOL(<ls_pedido>).
<ls_pedido>-netwr = <ls_pedido>-netwr * 2.
ENDLOOP.
DATA(lv_n) = lines( lt_pedidos ).
CLEAR lt_pedidos.SORT it_pedidos BY vbeln.
READ TABLE it_pedidos
WITH KEY vbeln = lv_vbeln BINARY SEARCH.
IF sy-subrc = 0.
lv_importe = it_pedidos-netwr.
ENDIF." Con una tabla SORTED o HASHED la búsqueda ya es rápida
lv_importe = VALUE #( lt_pedidos[ vbeln = lv_vbeln ]-netwr
OPTIONAL ).Los subtotales con AT END OF obligaban a ordenar antes y a que el campo estuviera a la izquierda de la estructura. Con GROUP BY eso deja de importar:
SORT lt_ventas BY cliente.
LOOP AT lt_ventas INTO ls_venta.
AT END OF cliente.
SUM.
WRITE: / ls_venta-cliente,
ls_venta-importe.
ENDAT.
ENDLOOP.TYPES ty_importe TYPE p LENGTH 15 DECIMALS 2.
LOOP AT lt_ventas INTO DATA(ls_venta)
GROUP BY ls_venta-cliente INTO DATA(lv_cliente).
DATA(lv_total) = REDUCE ty_importe(
INIT s = 0
FOR v IN GROUP lv_cliente
NEXT s = s + v-importe ).
cl_demo_output=>write( |{ lv_cliente }: { lv_total }| ).
ENDLOOP.
cl_demo_output=>display( ).Texto original del manual: sintaxis clásica
Corrección: la versión anterior de esta página llamaba «Native SQL» a estas sentencias, pero son las sentencias de tablas internas. Native SQL es el SQL propio de cada base de datos, que se escribe entre EXEC SQL y ENDEXEC (o con la clase CL_SQL_STATEMENT) y se salta las comprobaciones de ABAP.
Estas sentencias trabajan con tablas internas, es decir, con datos que están en la memoria del programa. Son: LOOP, READ TABLE, DESCRIBE TABLE, MODIFY, WRITE TO, DELETE, SORT, CLEAR, REFRESH, FREE.
- LOOP AT tabla_interna [ INTO área_trabajo ] [ FROM índice1 ] [ TO índice2 ][ WHERE condición ] [ TRANSPORTING NO FIELDS ]. … ENDLOOP.
Lee entradas de una tabla interna, recorriéndola en un bucle. Cada entrada se lee y se envía a un área de trabajo. Con INTO se indica un área de trabajo distinta a la cabecera de la tabla (el área por defecto). Si no hay entradas en la tabla no se entra en el bucle, y se tendrá SY-SUBRC = 4 (también si ninguna entrada cumple las condiciones del WHERE). En cada vuelta, el campo SY-TABIX contiene el índice de la entrada actual. Con FROM y TO se delimita el rango de entradas a procesar, de una inicial a otra final. Con TRANSPORTING NO FIELDS no se copia la entrada procesada en el área de trabajo (se gana velocidad si por ejemplo sólo se desea contar el número de entradas, no procesarlas). Esta opción se puede usar junto con WHERE pero nunca con INTO. Con la sentencia CONTINUE puede pasarse a la vuelta siguiente, y con EXIT se abandona el bucle. - READ TABLE tabla_interna [ INTO at ] { INDEX i | WITH KEY c1 = v1 ... cn = vn | WITH KEY = valor | WITH KEY clave } [ BINARY SEARCH ][ COMPARING c1 ... cn | COMPARING ALL FIELDS ][ TRANSPORTING c1 ... cn | TRANSPORTING NO FIELDS ].
Lee una línea de una tabla interna, a través de un índice o una clave (es más rápido que la búsqueda mediante LOOP … WHERE). Si se encuentra una entrada, devuelve SY-SUBRC = 0 y en SY-TABIX el índice de esa entrada; y SY-SUBRC = 4 en otro caso. Ocurre un error en ejecución si el índice a buscar es negativo; si es mayor que el número de entradas, se retorna SY-SUBRC = 4. Con INTO puede especificarse un área de trabajo distinta a la cabecera de la tabla. - DESCRIBE TABLE tabla_interna [ LINES nr_líneas ] [ OCCURS occ ].
Pregunta información al sistema respecto a la tabla interna especificada. Con LINES se actualiza en la variable ‘nr_líneas’ el número de líneas de dicha tabla (esto es útil por ejemplo para saber si la tabla está vacía o no, antes de intentar mostrarla por pantalla). Con OCCURS, guarda en ‘occ’ el número de entradas con que está definida la tabla. - MODIFY tabla_interna [ FROM área_trabajo ] [INDEX índice ] [ TRANSPORTING c1 ... cn | (campos) [ WHERE condición ] ].
Modifica el contenido de una entrada de una tabla interna, a partir de la información del área de trabajo especificada con FROM, o por defecto de la cabecera. Con INDEX se especifica la entrada a modificar (entonces no es necesario escribir el MODIFY dentro de un LOOP), o bien se busca dicha entrada con la condición del WHERE. Con TRANSPORTING sólo se actualizan los campos especificados (que se pueden dar de forma dinámica). - WRITE campo[offset1] TO tabla_interna[offset2] INDEX índice.
Actualiza la entrada de índice especificado de la tabla interna dada. Los offset permiten escribir en sólo una parte de la entrada de la tabla, y se escriben así: +número, todo pegado al nombre del campo. La sentencia similar WRITE … TO pero sin INDEX sólo sirve para variables simples. - DELETE tabla_interna [ INDEX índice ] [ FROM índice1 TO índice2 ] [ WHERE cond ][ ADJACENT DUPLICATES FROM itab [ COMPARING { f1...fn | ALL FIELDS } ] ].
Borra entradas de una tabla interna. Sin INDEX se usa dentro de un LOOP, y con INDEX fuera. Con ADJACENT DUPLICATES borra las entradas duplicadas conservando la primera. Dos líneas son consideradas duplicadas si sus campos clave son iguales, o bien si los campos f1 ... fn son iguales (si se especifican estos), o bien si todos sus campos son iguales (con ALL FIELDS). - SORT tabla_interna [ ASCENDING | DESCENDING ] [ AS TEXT ][ BY campo1 [ASCENDING | DESCENDING ] AS TEXT ] … [ BY campon [ASCENDING | DESCENDING ] AS TEXT ].
Ordena una tabla interna por los criterios especificados. Por defecto, de forma ascendente respecto a los campos clave. Para ordenar por otro(s) campo(s), indicarlo con BY. - CLEAR tabla_interna | campo.
Limpia o borra el área de trabajo de la tabla (la cabecera), pero no modifica ninguna entrada de la misma. También es aplicable a una variable simple (CLEAR está sobrecargado), y su efecto es borrarla (la pone a su valor inicial: SPACE o cero). - REFRESH tabla_interna.
Limpia el contenido de la tabla interna, borrando todas sus entradas, pero preservando el valor de su cabecera. - FREE tabla_interna | campo.
Libera el espacio ocupado en memoria por la tabla interna. También es aplicable a variables simples, destruyéndolas.
6.3. SENTENCIAS AT:
- Las sentencias AT permiten controlar el procesamiento de una tabla interna, dentro de un LOOP. Sólo se usan por tanto dentro de bucles LOOP. Permiten ejecutar condicionalmente un bloque de sentencias, dentro del bucle, según se cumpla o no una u otra condición. Son: AT NEW, AT END OF, AT FIRST, AT LAST.
- AT NEW campo. … ENDAT.
Con AT NEW se ejecuta el bloque cuando el campo especificado tiene un nuevo valor, es decir, que se activa al cambiar el campo su valor (se activa también en la primera vuelta, pues el campo pasará de INITIAL a su primer valor, pero no en la última vuelta, pues el campo no variará su valor). La tabla debe estar ordenada por ese campo y por los que están a su izquierda en la estructura, porque un cambio en cualquiera de ellos también activa el bloque. - AT END OF campo. … ENDAT.
Con AT END OF se ejecuta el bloque cuando el campo especificado alcanza su último valor. La tabla debe estar ordenada por ese campo y por los que están a su izquierda en la estructura, porque un cambio en cualquiera de ellos también activa el bloque. - AT FIRST.…ENDAT.
Con AT FIRST se ejecuta el bloque en la primera vuelta del LOOP. - AT LAST.…ENDAT.
Con AT LAST se ejecuta el bloque en la última vuelta del LOOP. Dentro de AT NEW y AT END OF, la sentencia SUM suma todos los campos numéricos de las entradas procesadas en estos bloques de proceso. Dentro de AT FIRST y AT LAST, suma los campos numéricos de todas las entradas de la tabla.
Ejemplo:ABAP SORT lt_ventas BY cliente. LOOP AT lt_ventas INTO ls_venta. AT END OF cliente. SUM. " subtotal de cada cliente WRITE: / ls_venta-cliente, ls_venta-importe. ENDAT. AT LAST. SUM. " total de toda la tabla WRITE: / 'Total', ls_venta-importe. ENDAT. ENDLOOP.
Para seguir
- Toda la sintaxis nueva, con más ejemplos de antes y ahora: New ABAP 7.5.
- Modelos de datos reutilizables y con semántica: vistas CDS.
- Lógica que no cabe en una consulta, ejecutada dentro de HANA: AMDP.
- Tablas de SAP explicadas campo a campo, con su SELECT moderno: referencia de tablas.