La ordenación virtual mediante claves secundarias permite acceder a tablas internas en diferentes órdenes sin duplicar datos ni reordenarlos físicamente.
Concepto
Una tabla interna puede tener:
- Clave primaria: Define el orden físico (para SORTED/HASHED)
- Claves secundarias: Proporcionan accesos alternativos
" Tabla con claves secundariasDATA: lt_orders TYPE SORTED TABLE OF ty_order WITH UNIQUE KEY order_id WITH NON-UNIQUE SORTED KEY by_customer COMPONENTS customer_id WITH NON-UNIQUE SORTED KEY by_date COMPONENTS order_date.Tipos de claves secundarias
SORTED KEY
" Clave secundaria ordenadaDATA: lt_data TYPE STANDARD TABLE OF ty_data WITH EMPTY KEY WITH NON-UNIQUE SORTED KEY by_name COMPONENTS name WITH NON-UNIQUE SORTED KEY by_amount COMPONENTS amount DESCENDING.
" Acceso por clave secundariaLOOP AT lt_data INTO DATA(ls_data) USING KEY by_name. " Iteración ordenada por nombreENDLOOP.
READ TABLE lt_data INTO DATA(ls_found) WITH KEY by_name COMPONENTS name = 'Test'.HASHED KEY
" Clave secundaria hash (acceso O(1))DATA: lt_data TYPE STANDARD TABLE OF ty_data WITH EMPTY KEY WITH UNIQUE HASHED KEY by_id COMPONENTS id.
" Acceso directo por hashREAD TABLE lt_data INTO DATA(ls_found) WITH KEY by_id COMPONENTS id = 12345.Ejemplos prácticos
Tabla de pedidos con múltiples accesos
TYPES: BEGIN OF ty_order, order_id TYPE i, customer_id TYPE i, product_id TYPE string, amount TYPE p DECIMALS 2, order_date TYPE d, status TYPE c LENGTH 1, END OF ty_order.
" Definición con múltiples clavesDATA: lt_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY " Acceso rápido por ID de pedido WITH UNIQUE HASHED KEY by_id COMPONENTS order_id " Búsqueda por cliente ordenada WITH NON-UNIQUE SORTED KEY by_customer COMPONENTS customer_id order_date " Búsqueda por producto WITH NON-UNIQUE SORTED KEY by_product COMPONENTS product_id " Ordenar por importe descendente WITH NON-UNIQUE SORTED KEY by_amount COMPONENTS amount DESCENDING.Uso de las diferentes claves
" Llenar tablalt_orders = VALUE #( ( order_id = 1 customer_id = 100 product_id = 'A' amount = '500.00' order_date = '20241101' status = 'A' ) ( order_id = 2 customer_id = 200 product_id = 'B' amount = '1500.00' order_date = '20241102' status = 'A' ) ( order_id = 3 customer_id = 100 product_id = 'A' amount = '300.00' order_date = '20241103' status = 'P' ) ( order_id = 4 customer_id = 300 product_id = 'C' amount = '2000.00' order_date = '20241104' status = 'A' ) ( order_id = 5 customer_id = 100 product_id = 'B' amount = '750.00' order_date = '20241105' status = 'A' )).
" 1. Buscar por ID (hash - O(1))READ TABLE lt_orders INTO DATA(ls_order) WITH KEY by_id COMPONENTS order_id = 3.
" 2. Todos los pedidos de un cliente (ordenados por fecha)LOOP AT lt_orders INTO ls_order USING KEY by_customer WHERE customer_id = 100. WRITE: / ls_order-order_id, ls_order-order_date.ENDLOOP.
" 3. Top pedidos por importeDATA(lv_count) = 0.LOOP AT lt_orders INTO ls_order USING KEY by_amount. lv_count = lv_count + 1. IF lv_count > 3. EXIT. ENDIF. WRITE: / 'Top', lv_count, ':', ls_order-amount.ENDLOOP.
" 4. Pedidos de un productoLOOP AT lt_orders INTO ls_order USING KEY by_product WHERE product_id = 'A'. WRITE: / ls_order-order_id, ls_order-product_id.ENDLOOP.Comparación de rendimiento
Sin claves secundarias (ineficiente)
" MALO: Búsqueda lineal O(n)LOOP AT lt_orders INTO DATA(ls_order) WHERE customer_id = 100. " ...ENDLOOP.
" MALO: Ordenar cada vezSORT lt_orders BY amount DESCENDING." Esto modifica el orden físico y pierde otros órdenesCon claves secundarias (eficiente)
" BIEN: Búsqueda binaria O(log n)LOOP AT lt_orders INTO DATA(ls_order) USING KEY by_customer WHERE customer_id = 100. " ...ENDLOOP.
" BIEN: Acceso ordenado sin modificar datosLOOP AT lt_orders INTO ls_order USING KEY by_amount. " Ordenado por amount sin SORTENDLOOP.Claves con múltiples componentes
DATA: lt_sales TYPE STANDARD TABLE OF ty_sales WITH EMPTY KEY WITH NON-UNIQUE SORTED KEY by_region_date COMPONENTS region sales_date WITH NON-UNIQUE SORTED KEY by_product_region COMPONENTS product_id region.
" Acceso con clave parcialLOOP AT lt_sales INTO DATA(ls_sale) USING KEY by_region_date WHERE region = 'EUROPE'. " Solo primer componente " Iteración de todas las ventas de Europa, ordenadas por fechaENDLOOP.Claves UNIQUE vs NON-UNIQUE
" UNIQUE: No permite duplicados en la combinación de componentesDATA: lt_unique TYPE STANDARD TABLE OF ty_data WITH UNIQUE HASHED KEY by_id COMPONENTS id.
" INSERT fallará si el ID ya existeINSERT VALUE #( id = 1 name = 'Duplicado' ) INTO TABLE lt_unique.IF sy-subrc <> 0. " Clave duplicadaENDIF.
" NON-UNIQUE: Permite duplicadosDATA: lt_non_unique TYPE STANDARD TABLE OF ty_data WITH NON-UNIQUE SORTED KEY by_category COMPONENTS category.
" Múltiples entradas con la misma categoría permitidasModificación de datos con claves secundarias
" Las claves secundarias se actualizan automáticamenteINSERT VALUE #( order_id = 6 customer_id = 100 amount = '999.00' ) INTO TABLE lt_orders." Todas las claves secundarias se actualizan
" MODIFY también actualiza las clavesMODIFY TABLE lt_orders FROM VALUE #( order_id = 1 amount = '600.00' " Cambio de amount) USING KEY by_id." La clave by_amount se reordena automáticamenteLimitaciones
" No se pueden usar en SORTED/HASHED TABLE con clave primaria conflictiva" INCORRECTO:DATA: lt_bad TYPE SORTED TABLE OF ty_data WITH UNIQUE KEY id WITH UNIQUE SORTED KEY also_id COMPONENTS id. " RedundanteCaso de uso: Índices múltiples
CLASS zcl_order_index DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_order, order_id TYPE i, customer_id TYPE i, status TYPE c LENGTH 1, amount TYPE p DECIMALS 2, END OF ty_order.
TYPES: ty_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY WITH UNIQUE HASHED KEY by_id COMPONENTS order_id WITH NON-UNIQUE SORTED KEY by_customer COMPONENTS customer_id WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.
METHODS: get_by_id IMPORTING iv_id TYPE i RETURNING VALUE(rs_order) TYPE ty_order.
METHODS: get_by_customer IMPORTING iv_customer_id TYPE i RETURNING VALUE(rt_orders) TYPE ty_orders.
METHODS: get_by_status IMPORTING iv_status TYPE c RETURNING VALUE(rt_orders) TYPE ty_orders.
PRIVATE SECTION. DATA: mt_orders TYPE ty_orders.ENDCLASS.
CLASS zcl_order_index IMPLEMENTATION. METHOD get_by_id. READ TABLE mt_orders INTO rs_order WITH KEY by_id COMPONENTS order_id = iv_id. ENDMETHOD.
METHOD get_by_customer. rt_orders = VALUE #( FOR order IN mt_orders USING KEY by_customer WHERE ( customer_id = iv_customer_id ) ( order ) ). ENDMETHOD.
METHOD get_by_status. rt_orders = VALUE #( FOR order IN mt_orders USING KEY by_status WHERE ( status = iv_status ) ( order ) ). ENDMETHOD.ENDCLASS.Notas importantes / Mejores prácticas
- Usa HASHED KEY para búsquedas por valor exacto (O(1)).
- Usa SORTED KEY para rangos y ordenación (O(log n)).
- Las claves secundarias ocupan memoria adicional.
- El mantenimiento de claves tiene coste en INSERT/MODIFY/DELETE.
- Considera el ratio lectura/escritura de tu aplicación.
- Para tablas pequeñas (< 100 filas), el beneficio puede ser mínimo.
- Combina con READ TABLE y LOOP AT.