✒️ABAP HANA / Las recomendaciones para desarrollar aplicaciones ABAP en SAP HANA Por Exequiel Acevedo
Las recomendaciones para desarrollar aplicaciones ABAP en SAP HANA

“FIND SELECT FOR POOL/CLUSTER TAB WITHOUT ORDER BY”
Este mensaje significa que en tu código estás haciendo un:
SELECT ... FROM [tabla pool o cluster] INTO ...
...sin usar una cláusula ORDER BY, y eso es potencialmente problemático. Pero más allá del ORDER BY, también está el tema de las tablas pool/cluster.
¿Qué son las tablas pool/cluster?
Son un tipo antiguo de tablas en SAP:
| Pooled Tables | Varias tablas lógicas almacenadas en una única tabla física. |
| Cluster Tables | Datos comprimidos de varias tablas almacenados juntos. |
Estas tablas no existen como "una tabla individual" en la base de datos. Son manejadas internamente por SAP, y no se comportan igual que tablas transparentes.
-
SAP HANA no admite bien las pooled/cluster tables.
En sistemas HANA, muchas de estas tablas se deben reemplazar por estructuras más modernas. -
El orden de los datos NO está garantizado.
Y como son estructuras especiales, el acceso puede ser impredecible. -
El rendimiento puede verse afectado.
Recomendación:
Un SELECT sin ORDER BY sobre tablas pool/cluster puede comportarse distinto entre bases de datos (MaxDB, Oracle, HANA...).-
Si usas un SELECT sobre una tabla pool/cluster, SIEMPRE deberías usar ORDER BY si el orden importa.
-
Mejor aún: evita usar tablas pool/cluster en desarrollos nuevos.
-
Migrar a tablas transparentes si es posible.
-
Usar CDS views o estructuras modernas en S/4HANA.
En resumen:ElementoSignificadoFIND SELECT FOR POOL/CLUSTER TAB WITHOUT ORDER BY Código hace SELECT sobre tabla especial (pooled/cluster) sin ORDER BY ¿Por qué es importante? Porque SAP HANA no garantiza orden ni soporta bien estas estructuras ¿Qué hacer? Agregar ORDER BY, o migrar a estructuras modernas
-
-
¿Usar la cláusula WHERE controla los datos transferidos?
Sí, totalmente.
La cláusula WHERE filtra los datos directamente en la base de datos, por lo tanto:
-
Reduce la cantidad de datos transferidos desde la BD al servidor de aplicación.
-
Mejora el rendimiento del programa (menos carga, menos memoria, más rápido).
-
Evita traer registros que no necesitas.
-
EJ: SELECT * FROM zclientes INTO TABLE @DATA(lt_clientes).
-------> Estás trayendo todos los registros de la tabla zclientes a tu programa, incluso si después solo usas unos pocos. - En cambio si utilizas :
- SELECT * FROM zclientes INTO TABLE @DATA(lt_clientes)
- WHERE pais = 'AR'.
Estás trayendo solo los clientes de Argentina, y el resto ni siquiera viaja por la red, ni consume memoria en ABAP.
Ventajas de usar WHERE
| Menor tráfico | Solo llegan al programa los registros que cumplen la condición. |
| Más rendimiento | La base de datos filtra más rápido que ABAP. |
| Menor uso de memoria | Tu tabla interna no se llena innecesariamente. |
| Código más claro | Estás especificando exactamente qué necesitas. |
¿La cláusula HAVING controla los datos transferidos como WHERE?
En parte sí, pero su función es diferente.
-
WHERE: Filtra registros individuales antes del agrupamiento (en la base de datos).
-
HAVING: Filtra después de hacer un GROUP BY, es decir, filtra grupos agregados.
Ambas reducen datos, pero en momentos distintos del procesamiento.
Diferencias clave entre WHERE y HAVING
| ¿Cuándo filtra? | Antes del GROUP BY | Después del GROUP BY |
| ¿Sobre qué actúa? | Filas individuales | Grupos (agregados) |
| ¿Usa funciones agregadas (SUM, COUNT, etc.)? | No | Sí |
| ¿Reduce datos transferidos? | Sí, directamente | Sí, pero después de agrupar |
¿HAVING también ayuda a reducir datos?
Sí, pero:
-
No reduce el número de filas leídas desde la tabla (como lo hace WHERE).
-
Filtra resultados agrupados, que también ahorra memoria y mejora rendimiento en el programa.
- HAVING siempre se utiliza junto con GROUP BY
Porque su propósito es precisamente filtrar los grupos que se crean después del GROUP BY.
Sin GROUP BY, no hay grupos que filtrar, así que HAVING no tendría sentido.
¿Entonces cómo lo pensamos?
| WHERE | Filas individuales | Siempre, con o sin GROUP BY |
| HAVING | Grupos agregados | Solo con GROUP BY |
¿los datos innecesarios de una tabla interna se determinan usando DELETE?
Sí, se pueden eliminar usando DELETE, pero lo ideal es evitarlos desde el principio.
Explicación:Hay dos enfoques:
1. Enfoque óptimo: evitar traer datos innecesarios
"Mejor no cargar lo que no vas a usar."
- Usá un SELECT con cláusula WHERE bien definida.
- Filtrás en la base de datos, no en el ABAP.
SELECT * FROM zclientes INTO TABLE @DATA(lt_clientes)
WHERE pais = 'AR' AND activo = 'X'.
- Aquí ya evitás cargar clientes inactivos o de otros países.
- Más rápido, más limpio, menos memoria.
Enfoque correctivo: limpiar después con DELETE
"Si ya los tenés en la tabla, podés eliminarlos."
DELETE lt_clientes WHERE activo <> 'X'.
Funciona, pero:
-
Ya trajiste datos innecesarios desde la base.
-
Usás más memoria de lo necesario.
- Tenés un paso extra en el código.
|
|
|
|
|
|
¿Para qué usamos UP TO n ROWS en ABAP?
La cláusula UP TO n ROWS se usa en una sentencia SELECT para limitar la cantidad de registros que se traen desde la base de datos.
✅ ¿Qué hace exactamente?
Te permite decirle al sistema:
“Traeme como máximo n registros.”
SELECT * FROM zventas
INTO TABLE @DATA(lt_ventas)
UP TO 10 ROWS.
Esto traerá máximo 10 registros desde la tabla zventas, sin importar cuántos cumplan la condición (si hay).
¿Por qué es útil?
|
Cargar pocos registros sin saturar memoria |
|
Vista previa de datos en ALV, reportes, etc. |
|
Evitar traer millones de registros innecesarios |
|
Cuando se implementa scroll o paginado en apps |
¡¡¡Pero ojo con algo importante:
¡UP TO n ROWS no garantiza orden!
Si querés, por ejemplo, los primeros 10 registros más recientes, tenés que usar ORDER BY también:
SELECT * FROM zventas
INTO TABLE @DATA(lt_top10)
WHERE estado = 'A'
ORDER BY fecha DESC
UP TO 10 ROWS.
- Esto ahora sí garantiza que traés las 10 ventas más recientes.
En CDS, se recomienda usar TOP y ORDER BY, no UP TO.
Pero en ABAP puro, UP TO n ROWS sigue siendo válido.
En resumen:
|
|
| ¿Reduce datos transferidos? |
|
| ¿Aumenta performance? |
|
| ¿Garantiza orden? |
|
Qué hace DISTINCT en ABAP?
La cláusula DISTINCT se utiliza en una sentencia SELECT para que el resultado no contenga registros duplicados.
Es decir, te devuelve solo combinaciones únicas de columnas seleccionadas.
-
SAP ejecuta una eliminación de duplicados en base a los campos seleccionados.
-
Solo se devuelven filas únicas para esa combinación de columnas.
Ejemplo práctico:Supongamos que zclientes tiene esto:
nombre ciudadAna Buenos Aires Ana Buenos Aires Luis Córdoba Ana Mendoza Sin DISTINCT:
SELECT nombre ciudad INTO TABLE @DATA(lt_clientes) FROM zclientes.
Resultado: 4 registros (incluyendo duplicados)
Con DISTINCT:
SELECT DISTINCT nombre ciudad INTO TABLE @DATA(lt_clientes) FROM zclientes.
Resultado tres registros.Se elimina el duplicado.
¿Cuándo usar DISTINCT?Cuándo usarlo Por quéConsideraciones importantesEvitar registros duplicados Cuando los datos traídos se repiten Para llenar listas sin repetición Ej. dropdowns de ciudades, países, etc. Al hacer estadísticas o conteos únicos Para contar sin duplicados -
DISTINCT actúa solo sobre los campos del SELECT.
Si incluís más campos, la probabilidad de duplicados baja (¡o desaparece!). -
Puede impactar en rendimiento.
Porque la base de datos debe ordenar y eliminar duplicados. Usalo cuando realmente lo necesites. -
No se puede usar DISTINCT con GROUP BY.
¿Qué son las funciones agregadas?
Están pensados para cosas diferentes.Las funciones agregadas (o funciones de agregación) son funciones que operan sobre un conjunto de registros y devuelven un único valor como resultado.
En vez de trabajar fila por fila, trabajan sobre grupos de datos.
Ejemplos comunes de funciones agregadas en ABAP (y SQL en general):Función ¿Qué hace?SUM( campo ) Devuelve la suma total de los valores AVG( campo ) Calcula el promedio MAX( campo ) Devuelve el valor máximo MIN( campo ) Devuelve el valor mínimo COUNT( * ) o COUNT( campo ) Cuenta la cantidad de registros
Ejemplo simple en ABAP:
Supongamos que tenés una tabla de ventas zventas.
-
SELECT SUM( monto ) AS total_ventas
FROM zventas
INTO @DATA(lv_total).
Esto te da un único valor: la suma total de todas las ventas. - ¿Dónde se usan?
-
Reportes y estadísticas.
-
Validaciones de negocio.
-
Dashboards (KPIs).
-
CDS views (con GROUP BY).
-
ALVs con totales.
Consideraciones importantes:Cuidado DetalleGROUP BY obligatorio Si pedís varios campos + funciones agregadas, debés agrupar No confundir con funciones escalares Las escalares (como UPPER, LEFT, CONCAT) operan fila por fila No devuelve múltiples resultados Siempre 1 valor por grupo o 1 total
-
-
En resumen:
| Funciones agregadas | Operan sobre un conjunto de filas para devolver un solo valor |
| Uso típico | SUM, AVG, COUNT, MIN, MAX |
| Muy comunes en | SELECTs con GROUP BY, reportes, CDS, análisis |
Escenario ficticio: Ventas por cliente
Supongamos que tenemos la siguiente tabla zventas con los campos:
| C001 | 100 | 2024-01-01 |
| C001 | 200 | 2024-02-15 |
| C002 | 300 | 2024-03-10 |
| C003 | 150 | 2024-01-20 |
| C001 | 250 | 2024-04-05 |
| C002 | 100 | 2024-04-02 |
Objetivo:
Mostrar por cada cliente:
-
Total de ventas (SUM)
-
Promedio de monto (AVG)
-
Monto más alto (MAX)
-
Monto más bajo (MIN)
-
Cantidad de operaciones (COUNT)
Codigo ABAP:
TYPES: BEGIN OF ty_resumen,
cliente_id TYPE zventas-cliente_id,
total TYPE p DECIMALS 2,
promedio TYPE p DECIMALS 2,
maximo TYPE p DECIMALS 2,
minimo TYPE p DECIMALS 2,
cantidad TYPE i,
END OF ty_resumen.
DATA: lt_resumen TYPE STANDARD TABLE OF ty_resumen.
SELECT
cliente_id,
SUM( monto ) AS total,
AVG( monto ) AS promedio,
MAX( monto ) AS maximo,
MIN( monto ) AS minimo,
COUNT( * ) AS cantidad
FROM zventas
GROUP BY cliente_id
INTO TABLE @lt_resumen.
LOOP AT lt_resumen INTO DATA(ls_cliente).
WRITE: / 'Cliente:', ls_cliente-cliente_id,
' Total:', ls_cliente-total,
' Promedio:', ls_cliente-promedio,
' Máximo:', ls_cliente-maximo,
' Mínimo:', ls_cliente-minimo,
' Cantidad:', ls_cliente-cantidad.
ENDLOOP.
¿Qué hace este código?
-
Agrupa las ventas por cliente.
-
Calcula 5 funciones agregadas.
-
Muestra un pequeño resumen para cada cliente.
-
Resultado esperado (en consola):
Cliente: C001 Total: 550.00 Promedio: 183.33 Máximo: 250.00 Mínimo: 100.00 Cantidad: 3
Cliente: C002 Total: 400.00 Promedio: 200.00 Máximo: 300.00 Mínimo: 100.00 Cantidad: 2
Cliente: C003 Total: 150.00 Promedio: 150.00 Máximo: 150.00 Mínimo: 150.00 Cantidad: 1
Vamos a convertir el ejemplo de ventas por cliente con funciones agregadas en una vista CDS (Core Data Services), ideal para exponer a Fiori, OData o simplemente reutilizar en ABAP.
Objetivo:Crear una CDS View que agrupe las ventas por cliente y muestre:
-
Total de ventas (SUM)
-
Promedio (AVG)
-
Máximo (MAX)
-
Mínimo (MIN)
-
Cantidad de ventas (COUNT)
Supongamos que la tabla base es zventas, con los campos:
cliente_id
- monto
- fecha
CDS View: ZI_VentasResumen
| @AbapCatalog.sqlViewName | Nombre técnico de la vista en la base de datos |
| define view ZI_VentasResumen | Nombre semántico en CDS |
| sum(...), avg(...), etc. | Funciones agregadas sobre los datos |
| group by cliente_id | Agrupamos por cliente |
| key cliente_id | Lo definimos como clave de la vista |
Reducir el número de ejecuciones de consulta (SELECTs) es una muy buena práctica.
¿Por qué?Cada vez que ejecutás una sentencia SELECT, el sistema hace una llamada al motor de base de datos, lo cual implica:
-
Tiempo de red (latencia)
-
Procesamiento en la BD
-
Transferencia de datos
-
Consumo de recursos (CPU, memoria)
Entonces, si podés hacer menos consultas, ¡tu código será más eficiente!
Mal ejemplo: muchos SELECT individuales
LOOP AT lt_clientes INTO DATA(ls_cliente).
SELECT SINGLE nombre INTO ls_cliente-nombre
FROM zclientes
WHERE cliente_id = ls_cliente-cliente_id.
" otras operaciones...
ENDLOOP.
SELECT a~cliente_id, a~venta_id, b~nombre
FROM zventas AS a
INNER JOIN zclientes AS b ON a~cliente_id = b~cliente_id
INTO TABLE @DATA(lt_ventas_con_nombre).
Opción 2: FOR ALL ENTRIES
|
Menos llamadas = más rápido |
|
Reduce presión en base de datos |
|
Menos repetición, más lógica en un solo paso |
|
Aprovecha code pushdown y procesamiento en la BD |
- Regla de oro:
"Traé todos los datos que necesitás en una sola consulta si es posible.
No hagas SELECTs dentro de loops."
Te dejo las técnicas más efectivas y prácticas, con ejemplos:
1. Usar campos indexados en la cláusula WHERE
Cuando hacés un SELECT, asegurate de filtrar por campos que tengan índice, como:
-
Claves primarias
-
Claves secundarias (índices definidos en SE11)
Si cliente_id y fecha están en un índice → muy rápido.
2. Evitar funciones sobre campos en el WHERE
Si aplicás funciones (como UPPER, +, LEFT, CAST, etc.) en los campos del WHERE, rompés el uso del índice.
Malo:3. Usar FOR ALL ENTRIES en lugar de SELECT dentro de LOOP
En vez de hacer 1000 SELECT SINGLE dentro de un LOOP, traé todo con un solo SELECT usando FOR ALL ENTRIES.
Bueno:4. Ordenar las condiciones del WHERE según los índices
Aunque SAP y la base de datos optimizan esto, poner los campos del índice primero ayuda a la claridad y optimización.
5. Usar SELECT SINGLE cuando solo necesitás una fila
No uses SELECT ... INTO TABLE si solo necesitás un único registro.
Correcto:6. Evitar traer más campos de los necesarios
Menos datos = menos carga = mejor rendimiento.
Malo:7. Analizar con ST05 (SQL Trace)
Usá la transacción ST05 para ver si tu consulta está usando un índice o está haciendo full table scan.
Te dice:
-
Qué índice usó
-
Cuántos registros leyó
-
Tiempo que tardó
En resumen:
| WHERE con campos indexados |
|
| Sin funciones sobre campos |
|
| Un solo SELECT con FOR ALL ENTRIES |
|
| Traer solo campos necesarios |
|
| Usar SELECT SINGLE con clave |
|
- Cuando hablamos de buffers en SAP, hay un concepto fundamental que aparece en los entornos con múltiples servidores de aplicación (sistemas distribuidos): los buffers cross-application server, también conocidos como:
Cross-Application Server Buffers (también llamados cross-instance buffers)
¿Qué son?
Los cross-application server buffers permiten que el buffer de una tabla esté disponible para todos los servidores de aplicación en un entorno SAP distribuido.
Es decir: si un dato se carga en un servidor SAP, otros servidores también pueden accederlo desde memoria, sin tener que consultar la base de datos de nuevo.
¿Por qué existen?
En entornos SAP grandes (como productivos), es común que haya varios servidores de aplicación SAP conectados a una sola base de datos:
Sin cross-buffering, cada servidor tendría su propia copia del buffer, y eso puede causar:
-
Inconsistencias (cada uno tiene su versión)
-
Accesos innecesarios a la base de datos
-
Mayor uso de memoria
¿Qué hace el cross-server buffering?
- Sincroniza el buffer entre todos los servidores de aplicación.
- Si un dato se actualiza en el servidor A, ese cambio se propaga a B, C, etc.
- Se mantiene la consistencia de los datos bufferizados en todo el landscape SAP.
¿Dónde se configura?
En SE11 → Technical Settings de la tabla:
Verás esta opción:
- Si está marcado: la tabla usa cross-application buffering.
- Si no: cada servidor mantiene su propio buffer (más rápido, pero menos consistente).
Cuándo usar cross-server buffering:
| Tablas de configuración global | Todos los servidores deben ver los mismos valores |
| Catálogos como países, monedas, unidades | Los cambios deben propagarse al instante |
| Tablas pequeñas y estables | Poca carga, mucho beneficio |
Cuándo NO usar cross-buffer:
| Tablas que cambian con frecuencia | Genera tráfico de sincronización innecesario |
| Alto volumen de escrituras | Impacta el rendimiento del buffer |
| Datos específicos por servidor | No tiene sentido compartirlos |
En resumen:
| Cross-server buffer | Hace que los datos bufferizados estén disponibles para todos los servidores de aplicación |
| ¿Dónde se configura? | En SE11 → Technical Settings |
| ¿Para qué sirve? | Mantener consistencia de datos en entornos distribuidos |
| ¿Cuándo usarlo? | Tablas pequeñas, de lectura frecuente y cambios poco frecuentes |
 
 
 
Sobre el autor
Publicación académica de Exequiel Eduardo Acevedo, en su ámbito de estudios para el Master ABAP for HANA.
Exequiel Eduardo Acevedo
Profesión: Analista de Sistemas de Información - Argentina - Legajo: JM18M
✒️Autor de: 32 Publicaciones Académicas
🎓Cursando Actualmente: Curso SAP ABAP
🎓Egresado de los módulos:
- Master Funcional ABAP
- Master ABAP Orientado a Objetos
- Master ABAP for HANA
- Curso SAP ABAP Avanzado
- Curso SAP ABAP
Disponibilidad Laboral: FullTime
























