Хранимая процедура не выполняется внутри цикла for из 100 000 строк - PullRequest
0 голосов
/ 08 октября 2019

У меня есть таблица с 100000 записей, и я должен сделать вычисления для каждой строки, я делаю вычисления со следующим SP

    PROCEDURE PROCESS_DATA(TOTAL_PREG_IN IN NUMBER, SIN_RESPUESTA_IN IN NUMBER, CORRECTAS_IN IN NUMBER, ERRONEAS_IN IN NUMBER, TIEMPO_IN IN VARCHAR2, NOTAS_DATOS_ID_IN IN NUMBER, ESQUEMA_IN IN NUMBER, FilasAfectadas OUT NUMBER)
    IS
    CALIFICACION NUMBER;
    REGLA NUMBER;
    LEVEL_ID NUMBER;
    TIEMPO_AUX VARCHAR2(5);
    TIEMPO NUMBER;
    e_div_zero EXCEPTION;

    BEGIN
        IF (CORRECTAS_IN IS NOT NULL AND ERRONEAS_IN IS NOT NULL) THEN

            IF (SIN_RESPUESTA_IN = 0 AND (CORRECTAS_IN + ERRONEAS_IN) = TOTAL_PREG_IN) THEN
                REGLA:=1;
                IF(TOTAL_PREG_IN = 0) THEN
                    RAISE e_div_zero;
                END IF;
                CALIFICACION := (CORRECTAS_IN * ESQUEMA_IN)/ TOTAL_PREG_IN;
            ELSE IF ((SIN_RESPUESTA_IN >= 1 AND SIN_RESPUESTA_IN <= 4) AND (CORRECTAS_IN + ERRONEAS_IN + SIN_RESPUESTA_IN) = TOTAL_PREG_IN) THEN
                REGLA := 2;
                IF(TOTAL_PREG_IN = 0) THEN
                    RAISE e_div_zero;
                END IF;
                CALIFICACION := (CORRECTAS_IN * ESQUEMA_IN)/ TOTAL_PREG_IN;
            ELSE IF ((SIN_RESPUESTA_IN = 5 OR SIN_RESPUESTA_IN = 10) AND (CORRECTAS_IN + ERRONEAS_IN + SIN_RESPUESTA_IN) = TOTAL_PREG_IN) THEN
                REGLA := 3;
                IF(CORRECTAS_IN + ERRONEAS_IN = 0) THEN
                    RAISE e_div_zero;
                END IF;
                CALIFICACION := (CORRECTAS_IN * ESQUEMA_IN)/(CORRECTAS_IN + ERRONEAS_IN);
            ELSE IF ((SIN_RESPUESTA_IN >= 6 AND SIN_RESPUESTA_IN <= 9) AND (CORRECTAS_IN + ERRONEAS_IN) = TOTAL_PREG_IN) THEN
                REGLA := 4;
                IF(TOTAL_PREG_IN = 0) THEN
                    RAISE e_div_zero;
                END IF;
                CALIFICACION := (CORRECTAS_IN * ESQUEMA_IN)/ TOTAL_PREG_IN;
            ELSE IF (CORRECTAS_IN + ERRONEAS_IN + SIN_RESPUESTA_IN != TOTAL_PREG_IN) THEN
                REGLA := 5;
                IF(CORRECTAS_IN + ERRONEAS_IN + SIN_RESPUESTA_IN = 0 ) THEN
                    RAISE e_div_zero;
                END IF;
                CALIFICACION := (CORRECTAS_IN * ESQUEMA_IN) / (CORRECTAS_IN + ERRONEAS_IN + SIN_RESPUESTA_IN );
            ELSE IF (SIN_RESPUESTA_IN > 10) THEN
                REGLA := 6;
                IF((CORRECTAS_IN + ERRONEAS_IN) > (TOTAL_PREG_IN/2))THEN
                    IF(CORRECTAS_IN + ERRONEAS_IN = 0 ) THEN
                        RAISE e_div_zero;
                    END IF;
                    CALIFICACION := (CORRECTAS_IN * ESQUEMA_IN) / (CORRECTAS_IN + ERRONEAS_IN);
                ELSE
                    CALIFICACION := ESQUEMA_IN * 0.8;
                END IF;
            ELSE IF (CORRECTAS_IN = 0 AND ERRONEAS_IN = 0) THEN
                REGLA := 7;
                SELECT LEVEL AS id, REGEXP_SUBSTR(TIEMPO_IN, '[^:]+', 1, LEVEL) AS data
                INTO LEVEL_ID, TIEMPO_AUX
                FROM dual where level = 2
                CONNECT BY REGEXP_SUBSTR(TIEMPO_IN, '[^:]+', 1, LEVEL) IS NOT NULL;

                TIEMPO := TO_NUMBER(TIEMPO_AUX);

                IF(TIEMPO >= 9) THEN
                    CALIFICACION := ESQUEMA_IN * 0.8;
                END IF;

            END IF;
            END IF;
            END IF;
            END IF;
            END IF;
            END IF;
            END IF;
        ELSE 
            REGLA:=7;
            SELECT LEVEL AS id, REGEXP_SUBSTR(TIEMPO_IN, '[^:]+', 1, LEVEL) AS data
                INTO LEVEL_ID, TIEMPO_AUX
                FROM dual where level = 2
                CONNECT BY REGEXP_SUBSTR(TIEMPO_IN, '[^:]+', 1, LEVEL) IS NOT NULL;

                TIEMPO := TO_NUMBER(TIEMPO_AUX);

                IF(TIEMPO >= 9) THEN
                    CALIFICACION := ESQUEMA_IN * 0.8;
                END IF;
        END IF;

        EXCEPTION 
        WHEN e_div_zero THEN
            calificacion := -1;

        INSERT INTO TABLA_AC AC (AC.AC_REGLA, AC.AC_FECHA_CALIFICACION, AC.AC_CALIFICACION, AC.AC_ARCHIVONOTAS_ID)
        VALUES (REGLA, TO_CHAR(SYSDATE, 'MM-DD-YYYY HH24:MI:SS'), CALIFICACION, NOTAS_DATOS_ID_IN );

        FilasAfectadas:= SQL%RowCount;
        COMMIT;
    END PROCESS_DATA;

И я вызываю этот sp внутри другого sp в цикле for

PROCEDURE MASIVE_PROCESS(FilasAfectadas_OUT OUT NUMBER)
IS
datos_notas t_row_datos_nota;
contador number := 0;
contador2 number := 0;
BEGIN
    SELECT nd.* bulk collect INTO datos_notas 
    FROM SGC_ARCHIVO_NOTAS_DATOS nd
    inner join SGC_ARCHIVO_NOTAS an ON an.AN_ID = nd.AND_ID_ARCHIVO;

    FOR idx IN datos_notas.FIRST..datos_notas.LAST
    Loop
        DB_ESCHEMA.PKG_TEST.PROCESS_DATA(
        TOTAL_PREG_IN        => datos_notas(idx).AND_TOTAL_PREG,
        SIN_RESPUESTA_IN     => datos_notas(idx).AND_SIN_RESPUESTA,
        CORRECTAS_IN         => datos_notas(idx).AND_CORRECTAS,
        ERRONEAS_IN          => datos_notas(idx).AND_ERRONEAS,
        TIEMPO_IN            => datos_notas(idx).AND_TIEMPO,
        NOTAS_DATOS_ID_IN        => datos_notas(idx).AND_ID,
        ESQUEMA_IN           => datos_notas(idx).AND_ESQUEMA_CALF,
        FILASAFECTADAS   => contador);
        contador2 := contador2 +1;
        DBMS_OUTPUT.put_line ('cont ' || contador2 );
    END LOOP;
    FilasAfectadas_OUT := contador;

END MASIVE_PROCESS;

Переменная counter2 достигает 100000 записей, но вставляются только 256 или 512 записей sp, которые выполняются в цикле for.

В чем проблема с этимкод

1 Ответ

0 голосов
/ 08 октября 2019

Исходя из отступа в вашем коде, похоже, что вы намерены заключить, что если есть ошибка деления на ноль, calificacion устанавливается в 1, а затем выполняется обработка исключений. Это не то, что вы написали. Все между EXCEPTION и END process_data является частью вашего обработчика исключений. Таким образом, вы вставляете в tabla_ac только в том случае, если произошла ошибка деления на ноль.

        EXCEPTION 
        WHEN e_div_zero THEN
            calificacion := -1;

        INSERT INTO TABLA_AC AC (AC.AC_REGLA, AC.AC_FECHA_CALIFICACION, AC.AC_CALIFICACION, AC.AC_ARCHIVONOTAS_ID)
        VALUES (REGLA, TO_CHAR(SYSDATE, 'MM-DD-YYYY HH24:MI:SS'), CALIFICACION, NOTAS_DATOS_ID_IN );

        FilasAfectadas:= SQL%RowCount;
        COMMIT;
    END PROCESS_DATA;

Я ожидаю, что вы хотели что-то подобное с дополнительным вложенным блоком PL / SQL

BEGIN
  BEGIN -- BEGIN the new nested PL/SQL block
    IF (CORRECTAS_IN IS NOT NULL AND ERRONEAS_IN IS NOT NULL) THEN
      <<rest of your code>>
    END IF;
  EXCEPTION 
    WHEN e_div_zero THEN
      calificacion := -1;
  END; -- END the nested PL/SQL block

  INSERT INTO TABLA_AC AC (AC.AC_REGLA, AC.AC_FECHA_CALIFICACION, AC.AC_CALIFICACION, AC.AC_ARCHIVONOTAS_ID)
    VALUES (REGLA, TO_CHAR(SYSDATE, 'MM-DD-YYYY HH24:MI:SS'), CALIFICACION, NOTAS_DATOS_ID_IN );

  FilasAfectadas:= SQL%RowCount;
  COMMIT;
END PROCESS_DATA;

Несколько других комментариев, не связанных с вашей непосредственной проблемой ..

1) Если вы хотите иметь оператор IF с несколькими ELSE условиями, используйте ELSIF вместо запуска нового IFзаявления. Таким образом, вы не получите полдюжины END IF операторов подряд. Гораздо разумнее сказать:

IF <<condition 1>> THEN
  <<do something>>
ELSIF <<condition 2>> THEN
  <<do something else>>
ELSIF <<condition 3>> THEN
  <<do a third thing>>
ELSE
  <<do a final thing>>
END IF;

2) Если у вас есть COMMIT в вашей процедуре, подавляющее большинство времени выполнения будет результатом попытки запустить 100 000 коммитов. Это займет в сотни раз больше времени, чем вставка 100 000 строк и выполнение одного коммита во внешней процедуре.

Похоже, что ваш код не предназначен для повторного входа, поэтому, вероятно, функционально проблематично иметь временные коммиты. Что произойдет, если вы обработали 50 000 строк и ваш код выдает исключение? У вас не было бы очевидного способа перезапустить процесс в строке 50,001, поэтому, если вы перезапустите цикл, вы получите 150 000 строк в tabla_ac, а не 100 000, что вряд ли будет тем, что вы хотели.

...