lunes, 14 de enero de 2008

Busquedas en LOOPS anidados

En ocasiones tendremos tablas internas muy pesadas, y deberemos recorrer una dentro de otra para hacer cualquier modificación. En este ejemplo, que hecho con una carga de datos razonable, selecciono las facturas de un mes en it_tab1, luego las de un día en it_tab2, y recorro it_tab2 dentro de it_tab1.

Suponiendo que en it_tab1 haya m entradas y en it_tab2 haya n, si lo hacemos sin más el número de vueltas sería m x n. Incluso, aunque hayamos puesto la cláusula WHERE.

En estos casos, donde además ambas tablas tengan los mismos campos clave, recomiendo usar un índice interno para empezar la búsqueda en la segunda tabla, de esta forma se reducirá el número de vueltas.

En el PERFORM EXCLUIR se puede ver cómo se ha hecho. El resto es paja necesaria.

*&---------------------------------------------------------------------*
*& Report ZINDICES
*&
*&---------------------------------------------------------------------*
*&
*& Un loop dentro de otro loop, con índices
*&---------------------------------------------------------------------*

REPORT zindices.


*&---------------------------------------------------------------------*
*& TOP
*& declaración de tablas, tipos, variables, indices, tablas internas.
*&---------------------------------------------------------------------*
TABLES:
vbrk,
vbrp.

RANGES: rg_fecha FOR vbrk-fkdat.


TYPES: BEGIN OF ty_tab,
"campos de vbrk
vbeln LIKE vbrk-vbeln,
vtweg LIKE vbrk-vtweg,
spart LIKE vbrk-spart,
erdat LIKE vbrk-erdat,
fkdat LIKE vbrk-fkdat,
"Campos de vbrp
posnr LIKE vbrp-posnr,
matnr LIKE vbrp-matnr,
fkimg LIKE vbrp-fkimg,
END OF ty_tab.

DATA:
it_tab1 TYPE TABLE OF ty_tab,
it_tab2 TYPE TABLE OF ty_tab,
it_tab3 TYPE TABLE OF ty_tab,
indice LIKE sy-tabix,
vbeln1 LIKE vbrk-vbeln,
vbeln2 LIKE vbrk-vbeln,
posnr1 LIKE vbrp-posnr,
posnr2 LIKE vbrp-posnr.

************************************************************************

*&---------------------------------------------------------------------*
*& SEL
*& Pantalla de selección
*& Elementos de texto:
*& text-001: "Opciones de selección"
*& text-002: "Opciones del índice"
*& p_anno --> año a elegir
*& p_mes --> mes a elegir
*& p_dia --> día a excluir
*&---------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK bl1 WITH FRAME TITLE text-001.

PARAMETERS:
p_anno(4) TYPE n obligatory,
p_mes(2) TYPE n obligatory,
p_dia(2) TYPE n obligatory.
SELECTION-SCREEN END OF BLOCK bl1.

SELECTION-SCREEN BEGIN OF BLOCK bl2 WITH FRAME TITLE text-002.
PARAMETERS:
p_vkorg LIKE vbrk-vkorg,
p_vtweg LIKE vbrk-vtweg.
SELECTION-SCREEN END OF BLOCK bl2.

************************************************************************



*&---------------------------------------------------------------------*
*& EVE
*& Eventos
*&---------------------------------------------------------------------*

START-OF-SELECTION.

PERFORM seleccion_t1 TABLES it_tab1. "Selección de vbrk-vbrp en it_tab1
PERFORM seleccion_t2 TABLES it_tab2. "Selección de vbrk-vbrp en it_tab2

PERFORM excluir
TABLES it_tab1 it_tab2 it_tab3. "it_tab3 = it_tab2 - it_tab1.

************************************************************************
*&---------------------------------------------------------------------*
*& F01
*& Subrutinas
*&---------------------------------------------------------------------*

*&---------------------------------------------------------------------*
*& Form seleccion_t1
*&---------------------------------------------------------------------*
* Selecciona las facturas y posiciones del año y mes elegido
*----------------------------------------------------------------------*
* -->P_TAB1 it_tab1
*----------------------------------------------------------------------*

FORM seleccion_t1 TABLES p_tab1 LIKE it_tab1.

DATA: lv_fecha_low LIKE sy-datum,
lv_fecha_high LIKE sy-datum.

CONCATENATE p_anno p_mes '01' INTO lv_fecha_low.
PERFORM buscar_ultimo_dia USING lv_fecha_low
CHANGING lv_fecha_high.

CLEAR rg_fecha.
MOVE 'I' TO rg_fecha-sign.
MOVE 'BT' TO rg_fecha-option.
MOVE lv_fecha_high TO rg_fecha-high.
MOVE lv_fecha_low TO rg_fecha-low.
APPEND rg_fecha. CLEAR rg_fecha.

READ TABLE rg_fecha INDEX 1.

SELECT
a~vbeln a~vtweg a~spart a~erdat a~fkdat
b~posnr b~matnr b~fkimg
INTO CORRESPONDING FIELDS OF TABLE p_tab1
FROM vbrk AS a
INNER JOIN vbrp AS b ON a~vbeln = b~vbeln
WHERE a~fkdat IN rg_fecha
AND a~vkorg = p_vkorg
AND a~vtweg = p_vtweg.


ENDFORM. " seleccion_t1
*&---------------------------------------------------------------------*
*& Form seleccion_t2
*&---------------------------------------------------------------------*
* Selecciona las facturas del día elegido
*----------------------------------------------------------------------*
* -->P_TAB2 it_tab2
*----------------------------------------------------------------------*
FORM seleccion_t2 TABLES p_tab2 LIKE it_tab2.

DATA: lv_fecha LIKE vbrk-fkdat.

CONCATENATE p_anno p_mes p_dia INTO lv_fecha.

SELECT
a~vbeln a~vtweg a~spart a~erdat a~fkdat
b~posnr b~matnr b~fkimg
INTO CORRESPONDING FIELDS OF TABLE p_tab2
FROM vbrk AS a
INNER JOIN vbrp AS b ON a~vbeln = b~vbeln
WHERE a~fkdat = lv_fecha
AND a~vkorg = p_vkorg
AND a~vtweg = p_vtweg.


ENDFORM. " seleccion_t2
*&---------------------------------------------------------------------*
*& Form excluir
*&---------------------------------------------------------------------*
* hacer que p_tab3 = p_tab1 - p_tab2
*----------------------------------------------------------------------*
* -->P_TAB1 it_tab1
* -->P_TAB2 it_tab2
* -->P_TAB3 it_tab3
*----------------------------------------------------------------------*
FORM excluir TABLES p_tab1 LIKE it_tab1
p_tab2 LIKE it_tab2
p_tab3 LIKE it_tab3.

*Ahora, tengo que eliminar de t_no_asis los pedidos y posiciones que ESTÉN en t_handling

*Ordeno las tablas
SORT p_tab1 BY vbeln posnr.
SORT p_tab2 BY vbeln posnr.

*Ahora, busco
CLEAR indice. ADD 1 TO indice.
LOOP AT p_tab1.
vbeln1 = p_tab1-vbeln.
posnr1 = p_tab1-posnr.
LOOP AT p_tab2 FROM indice.
ADD 1 TO indice.
vbeln2 = p_tab2-vbeln.
posnr2 = p_tab2-posnr.
IF vbeln2 > vbeln1 OR
( vbeln2 EQ vbeln1 AND posnr2 > posnr1 ).
EXIT.
ELSE.
IF vbeln2 EQ vbeln1 AND posnr2 EQ posnr1.
MOVE-CORRESPONDING p_tab1 TO p_tab3.
MOVE-CORRESPONDING p_tab2 TO p_tab3.
APPEND p_tab3. CLEAR p_tab3.
ENDIF.
ENDIF.
ENDLOOP.
ENDLOOP.

ENDFORM. " excluir
*&---------------------------------------------------------------------*
*& Form buscar_ultimo_dia
*&---------------------------------------------------------------------*
* Busca el último día de un mes.
*----------------------------------------------------------------------*
* -->P_FECHA_LOW Primer día del mes
* <--P_FECHA_HIGH Último día del mes
*----------------------------------------------------------------------*
FORM buscar_ultimo_dia USING p_fecha_low
CHANGING p_fecha_high.

CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
day_in = p_fecha_low
IMPORTING
last_day_of_month = p_fecha_high
* EXCEPTIONS
* DAY_IN_NO_DATE = 1
* OTHERS = 2
.
IF sy-subrc <> 0.
* MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
* WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.


ENDFORM. " buscar_ultimo_dia

5 comentarios:

Unknown dijo...

Hola, acabo de descubrir este blog y no llevo mucho tiempo programando en abap, pero desde el primer dia me han dicho que NUNCA haga LOOPs anidados, dan muchos problemas, es mejor dentro de un LOOP hacer un read table

Anónimo dijo...

Hola men, lamento el pseudodigo q insertas es bueno; esperaba encontrar algo mas novedoso pero lamento decirk}te ke esta forma de busqkeda evitando vueltas es conocida hace siglos como matching.

y se puede usar entre 3 tablas o arreglos (en otros lenguajes).

Gracias, sin resentimientos.

culanas dijo...

Hola Juan,
un read solo hace una lectura, los loops anidados recorren toda la tabla. Por eso en ocasiones serán necesarios.

Hola c_cobol. No es pseudocódigo, está escrito en ABAP, se puede compilar y ejecutar (si tienes las tablas activas en el DD)

En cuanto el propósito de este blog, es recordarme a mí mismo cosas que me costó encontrar.

Gracias a los dos por comentar (aunque no suelo ni leer ni responder a los comentarios)

CARLOS PANDURO dijo...

Interesante el código... estoy iniciando abap. y me gustaría encontrar código de POO en abap, si tienes un ejemplo me seria de mucha ayuda... gracias de antemano.

Luis dijo...

Estimado:
ahora que me estoy metiendo a la programacion abap, como seria este ejercicio.
1. Crear una tabla y su cabecera( estructura ).

2. Realizar un concatenate del campo BELNR GJAHR de la tabla BKPF y mostrar los 20 primeros registros.

3. Realizar una impresión en write de cada uno de los registros de la tabla de BD BKPF con los campos gjahr, belnr, bukrs, blart, xblnr. Tener en cuenta estas condiciones al momento de imprimir:

a. Si el blart = WE mostrarlo en color 1.

b. Si el XBLNR contiene el símbolo ‘-’ mostrarlo en color 2.

c. Si GJAHR es mayor a 2010 mostrarlo en color 3.

d. Si GJAHR es menor a 2010 mostrarlo en color 4.