rownum не работает для обновления в PL / SQL, содержащем запрос на обновление - PullRequest
0 голосов
/ 14 февраля 2019

У меня есть в общей сложности 193267 записей, которые нужно обновить, и это нужно сделать в пакете из 10 000 записей, однако обновление не принимает значение rownum

Это не занимает 10 000 номеров в пакете.

set serveroutput ON
DECLARE
    ROWNUM NUMBER := 0;
BEGIN
    LOOP
        UPDATE billing.account_country
        SET    contract_type_id = NULL
        WHERE  ROWNUM <= 10000
               AND mdate < SYSDATE - 300
               AND mdate >= SYSDATE - 500
               AND id IS NOT NULL
               AND id IN ( 209 )
               AND contract_type_id < 1000;

        ROWNUM := SQL%rowcount;

        dbms_output.Put_line('row num:'
                             ||ROWNUM);

        IF ( ROWNUM = 0 ) THEN
          EXIT;
        END IF;
    END LOOP;

    dbms_output.Put_line('done..');
END; 

результат будет следующим:

row num:193267
row num:0
done..

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Если ваше требование состоит в том, чтобы ваши коммиты не превышали 10 000 записей на коммит (но коммитам по-прежнему разрешается быть меньше, чем 10 000 записей на коммит), то вам повезло.

Вам не нужно управлять своим собственным размером коммита или вообще ставить в очередь.
Oracle имеет встроенные утилиты для такого рода вещей.

Ниже я добавлю пример обработки блока 10K самостоятельно, но простое использование встроенных функций Oracle сэкономит время и позволит избежать ошибок.

Вот пример, в котором используется DBMS_PARALLEL_EXECUTE (вы можете установить степень параллельности на 1, если вы на самом деле не хотите много распараллеливать).

Примечательными элементами здесь являются CHUNK_SIZE => 10000 для ограничения размера коммита, PARALLEL_LEVEL => 1 для ограничения параллели и ROWID BETWEEN :STARD_ID AND :END_ID (также ROWNUM был удален, но остальная часть вашего оригиналазапрос был оставлен как есть, включая элементы ID IS NOT NULL и ID IN)

BEGIN
  DBMS_PARALLEL_EXECUTE.CREATE_TASK(TASK_NAME => 'NULL_CONTRACT_TYPE');

  DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_ROWID(
    TASK_NAME => 'NULL_CONTRACT_TYPE' ,
    TABLE_OWNER => 'BILLING',
    TABLE_NAME => 'ACCOUNT_COUNTRY', 
    BY_ROW => TRUE, 
    CHUNK_SIZE => 10000);

  DBMS_PARALLEL_EXECUTE.RUN_TASK(
    TASK_NAME => 'NULL_CONTRACT_TYPE' ,
    SQL_STMT => 'UPDATE account_country SET contract_type_id = NULL ' ||
                'WHERE  mdate < SYSDATE - 300 ' ||
                'AND mdate >= SYSDATE - 500 ' ||
                'AND id IS NOT NULL AND id IN ( 209 ) ' ||
                'AND contract_type_id < 1000 ' ||
                'AND ROWID BETWEEN :START_ID AND :END_ID',
    LANGUAGE_FLAG => DBMS_SQL.NATIVE, 
    PARALLEL_LEVEL => 1);

  DBMS_PARALLEL_EXECUTE.DROP_TASK(TASK_NAME => 'NULL_CONTRACT_TYPE');

END;
/

Затем Oracle сделает все остальное за вас - фиксация в указанных блоках, обновление только целевых записейи т. д.

Но, если вы все еще хотите сделать это самостоятельно, один из подходов - извлечь из курсора и ограничить количество целевых строк.Ниже приведен пример этого (как и раньше, я оставил ваш исходный запрос без изменений).Из вашего исходного запроса может показаться, что ID не уникален, поэтому я использую ROWID s для этого примера.

DECLARE
  TYPE ROWID_LIST IS TABLE OF ROWID;
  V_UPDATE_TARGETS ROWID_LIST := ROWID_LIST();
  CURSOR UPDATE_ACCOUNT_COUNTRY_TARGETS IS (
    SELECT ROWID FROM ACCOUNT_COUNTRY
    WHERE  mdate < SYSDATE - 300
       AND mdate >= SYSDATE - 500
       AND id IS NOT NULL AND id IN ( 209 )
       AND contract_type_id < 1000);
BEGIN
OPEN UPDATE_ACCOUNT_COUNTRY_TARGETS;
LOOP
  EXIT WHEN UPDATE_ACCOUNT_COUNTRY_TARGETS%NOTFOUND;
  FETCH UPDATE_ACCOUNT_COUNTRY_TARGETS BULK COLLECT INTO V_UPDATE_TARGETS LIMIT 10000;
  FORALL ROWID_INDEX IN 1..V_UPDATE_TARGETS.COUNT
    UPDATE ACCOUNT_COUNTRY
    SET CONTRACT_TYPE_ID = NULL
    WHERE ROWID = V_UPDATE_TARGETS(ROWID_INDEX);
  COMMIT;
END LOOP;
CLOSE UPDATE_ACCOUNT_COUNTRY_TARGETS;
END;
/
0 голосов
/ 14 февраля 2019

Я не вижу проблемы в вашем коде - работает для меня:

DECLARE
    i           NUMBER;

    sqlCreate   VARCHAR2 (4000)
                    := 'CREATE TABLE MYTABLE (mycolumn VARCHAR2(100))';

    sqlDrop     VARCHAR2 (4000) := 'DROP TABLE MYTABLE';
BEGIN
    EXECUTE IMMEDIATE sqlCreate;

    FOR x IN 1 .. 10
    LOOP
        EXECUTE IMMEDIATE 'insert into MYTABLE values(' || x || ')';
    END LOOP;

    LOOP
        EXECUTE IMMEDIATE 'UPDATE MYTABLE r
           SET r.mycolumn = r.mycolumn || ''x''
         WHERE ROWNUM <= 4
           AND not r.mycolumn like ''%x%''';

        i := SQL%ROWCOUNT;
        DBMS_OUTPUT.put_line ('i: ' || i);

        IF (i = 0)
        THEN
            EXIT;
        END IF;
    END LOOP;

    EXECUTE IMMEDIATE sqlDrop;
END;

Вывод:

i: 4
i: 4
i: 2
i: 0

Возможно

`AND contract_type_id < 1000;`

..недостаточно.Разве не должно быть

AND NOT contract_type_id is null;

?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...