#1
|
|||
|
|||
Necesito hacer un INNER JOIN de 3 tablas eficiente
Tengo 3 tablas:
LFA1: - lifnr (Clave) - land1 - ... LFB1: - lifnr (Clave) - bukrs (Clave) - pernr - ... LFBK: - lifnr (Clave) - banks (Clave) - bankl (Clave) - bankn (Clave) - bkont - ... lo que necesito es un INNER JOIN entre LFA1 y LFB1 en lifnr y ésta un LEFT OUTER JOIN con la LFBK en lifnr Mi primera solución fue: select lifnr land1 .... from LFA1 as a INNER JOIN LFB1 as b on .... OUTER JOIN .... pero me dijeron que era ineficiente, que usara FOR ALL ENTRIES entonces implementé lo siguiente: El gran problema es que el campo lifnr NO es clave de la tabla LFB1 por lo tanto debo anidar un loop, y además para la condición LEFT OUTER JOIN debo anidar otro loop ya que lifnr tampoco es clave de LFBK. Como imaginarán, esto tarda un poco mas que una eternidad. Existe una solución mas eficiente???, cuando usamos FOR ALL ENTRIES y los campos que accedemos no son claves (No podemos usar READ TABLE) y por lo tanto debemos anidar loops. Además de la ineficiencia, con la condición LEFT OUTER JOIN se me plantéo que el APPEND del loop mas interno debería hacer dentro del loop si consiguió algún registro, SINO fuera (con las campos NULL) si no consiguio ningún registro. Estaría bien, eso? Saludos, Gracias |
#2
|
||||
|
||||
Lo mejor que puedes hacer, es chequear la eficiencia en SE30. Accede a esa transacción y pulsa en TIPS & TRICKS. Una vez dentro, declara las variables etc. y pon los códigos con los INNER y con el código LOOP y lo comparas... no olvides ejecutarlo varias veces, puesto que cambia en ocasiones dependiendo de la conexión a la BBDD los índices etc..
A ver si te vale eso! Suerte. Javier. |
#3
|
|||
|
|||
Gracias por tu ayuda,
calcule el performance, y el resultado fue: Usando Select .... INNER JOIN .... LEFT JOIN ....: 33 seg, 95%bd resto Abap y sistema Usando For All Entries: 797 seg(24 veces mas lento) y aproxmadamente 98% Abap y resto sistema y BD. Es increible la diferencia, mi duda es cuando es mas performante un INNER JOIN que un FOR ALL ENTRIES? Mi hipotesis: Calculo que es mejor un FOR ALL ENTRIES cuando accedemos por claves a las tablas (Y podemos usar READ TABLE), el cuál no es mi caso, y por lo tanto debo anidar 3 loops. |
#4
|
||||
|
||||
Esto depende mucho también de las tablas etc. Por ejemplo, las tablas de documentos de material son tablas muy pesadas, y la selección aun accediendo por clave, si la tabla que usas para el FOR ALL ENTRIES es muy pesada, va a dar también un tiempo de respuesta demasiado alto... Lo mejor para optimizarlo es en primer lugar, traer solo los datos que necesitas... el SELECT * en tablas con bastantes datos puede hacer lento el acceso a BBDD, mientras que igual solo necesitas 20 o 30 campos de esa tabla... por otro lado, a mi personalmente me gusta hacer el acceso a base de datos de una vez, y luego tratar los datos recuperados. En tu caso, yo creo que podría ser factible una mezcla de ambas cosas... hacer el LEFT OUTER JOIN por un lado (Para evitar anidad dos bucles) y recuperar el resto de la información por otro lado... y una vez que lo tengas en ambas tablas, montar la tabla final... pero esto es cuestión también de probarlo!!
Suerte pues!! Y ojalá encuentres la solución más optima! Un Saludo, Javier. |
#5
|
|||
|
|||
¿Compruebas que el primer SELECT devuelva datos antes de ejecutar el FOR ALL ENTRIES? En caso de no hacerlo, si no hay datos el programa baja TODA LA TABLA, ignorando totalmente el WHERE... a partir de ahí, todo se va al carajo.
La elección entre JOIN, FOR ALL ENTRIES y subqueries (LOOP-SELECT) debe hacerse con cuidado, y teniendo en cuenta muchos factores, como el tamaño de las tablas, los índices utilizables, la velocidad y la carga del servidor de datos... En tu caso yo haría una tercera prueba: INNER JOIN + FOR ALL ENTRIES.
__________________
"Porque algunos sabemos que somos parte del problema"
|
#6
|
|||
|
|||
Hola MarianitoSAPO...
LIFNR si es llave en las tres tablas... y a menos que tengan activos índices diferentes, si puedes usar ese campo como llave y hasta me atrevería a decirte que sería mucho mas rápido un INNER JOIN. Por otro lado, es REGLA INVIOLABLE asegurarse SIEMPRE antes de hacer un FOR ALL ENTRIES que la tabla usada para ello no venga VACÍA!!! Y por último, el código está muy mal hecho!!! No hay ninguna necesidad de usar esos LOOPS anidados!!! No necesitas usar ese AUX2 para hacerle ese LOOP a AUX3 ya que el LIFNR es el mismo en todas desde AUX1. por lo tanto podrías dejarlo así: LOOP AUX1. LOOP AUX2 WHERE LIFNR = AUX1-LIFNR ENDLOOP. LOOP AUX3 WHERE LIFNR = AUX1-LIFNR ENDLOOP. ENDLOOP. Además, podrías hacerle un SORT a todas tus tablas por el campo LIFNR antes de hacer los LOOP's e ir borrando los registros ya leídos para ir reduciendo el tamaño de las tablas internas: SORT: AUX1, AUX2, AUX3 by LIFNR. LOOP a AUX1. LOOP a AUX2 WHERE LIFNR = AUX1-LIFNR. <Realiza operaciones> DELETE AUX2. ENDLOOP. LOOP a AUX3 WHERE LIFNR = AUX1-LIFNR <Realiza operaciones> DELETE AUX3. ENDLOOP. ENDLOOP. Y si aún quieres mas velocidad, entónces mete tus tablas y tus work areas en FIELD-SYMBOLS y has los LOOPS con ellos: FIELD-SYMBOLS: <AUX1> like aux1, <W_AUX1> like line of aux1, <AUX2> like aux2, <W_AUX2> like line of aux2, <AUX3> like aux3, <W_AUX3> like line of aux3. ASSIGN: aux1 TO <AUX1>, aux2 TO <AUX2>, aux3 TO <AUX3>. LOOP AT <AUX1> ASSIGNING <W_AUX1>. LOOP AT <AUX2> ASSIGNING <W_AUX2>. <Realiza operaciones> DELETE <AUX2>. ENDLOOP. LOOP AT <AUX3> ASSIGNING <W_AUX3>. <Realiza operaciones> DELETE <AUX3>. ENDLOOP. ENDLOOP. Saludos!!! |
Herramientas | Buscar en Tema |
Desplegado | |
|
|