Necesito hacer un INNER JOIN de 3 tablas eficiente

Colapsar
X
 
  • Tiempo
  • Mostrar
Limpiar Todo
nuevos mensajes
  • MarianitoSAPO
    Junior Member
    • oct
    • 7

    #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:

    Código:
    SELECT lifnr land1 name1 name2 ktokk kunnr loevm
             sperr stcd1 stcd2 vbund stceg profs
      FROM lfa1
      INTO TABLE i_aux1.
    
      SELECT lifnr bukrs loevm sperr
      FROM lfb1
      INTO TABLE i_aux2
      FOR ALL ENTRIES IN i_aux1
      WHERE lifnr = i_aux1-lifnr.
    
      SELECT lifnr banks bankl bankn bvtyp bkont
      FROM lfbk
      INTO TABLE i_aux3
      FOR ALL ENTRIES IN i_aux2
      WHERE lifnr = i_aux2-lifnr.
    
      " EXTREMADAMENTE INEFECIENTE
      LOOP AT i_aux1 INTO r_aux1.
        r_maestros-lifnr = r_aux1-lifnr.
        r_maestros-land1 = r_aux1-land1.
        r_maestros-name1 = r_aux1-name1.
        r_maestros-name2 = r_aux1-name2.
        r_maestros-ktokk = r_aux1-ktokk.
        r_maestros-kunnr = r_aux1-kunnr.
        r_maestros-loevm = r_aux1-loevm.
        r_maestros-sperr = r_aux1-sperr.
        r_maestros-stcd1 = r_aux1-stcd1.
        r_maestros-stcd2 = r_aux1-stcd2.
        r_maestros-vbund = r_aux1-vbund.
        r_maestros-stceg = r_aux1-stceg.
        r_maestros-profs = r_aux1-profs.
    
        "Usamos un loop ya que no accedemos mediante clave
        LOOP AT i_aux2 INTO r_aux2 WHERE lifnr = r_aux1-lifnr.
          r_maestros-bukrs = r_aux2-bukrs.
          r_maestros-so_loevm = r_aux2-so_loevm.
          r_maestros-so_sperr = r_aux2-so_sperr.
    
          "Condicion LEFT OUTER JOIN
          LOOP AT i_aux3 INTO r_aux3 WHERE lifnr = r_aux2-lifnr.
            r_maestros-banks = r_aux3-banks.
            r_maestros-bankl = r_aux3-bankl.
            r_maestros-bankn = r_aux3-bankn.
            r_maestros-bvtyp = r_aux3-bvtyp.
            r_maestros-bkont = r_aux3-bkont.
    
            " VER si este append no deberia ir tambien fuera de este loop
            " en el caso de que no se ingrese a este LOOP
            APPEND r_maestros TO i_maestros.
    
          ENDLOOP.
    
        ENDLOOP.
      ENDLOOP.
    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
  • ppchico
    Senior Member
    • oct
    • 144

    #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.

    Comentario

    • MarianitoSAPO
      Junior Member
      • oct
      • 7

      #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.

      Comentario

      • ppchico
        Senior Member
        • oct
        • 144

        #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.

        Comentario

        • VLozano
          Senior Member
          • nov
          • 124

          #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"
          - Pequeña guía para escribir mensajes en foros técnicos -

          Comentario

          • cubanito
            Junior Member
            • nov
            • 13

            #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!!!

            Comentario

            Trabajando...