我总共有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..
答案 0 :(得分:0)
我在您的代码中没有看到问题-对我有用:
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;
?
答案 1 :(得分:0)
如果您的要求是每次提交的提交不超过10,000条记录(但是仍允许每次提交的提交少于10,000条记录),那么您很幸运。
您不需要管理自己的提交大小,也不需要考虑排队。
Oracle具有用于此类事情的内置实用程序。
我将在下面添加一个自己处理10K块的示例,但是仅使用Oracle的内置函数将节省时间并避免错误。
下面是使用 DBMS_PARALLEL_EXECUTE
的示例(如果您实际上不希望进行太多并行化,则可以将 parallel degree 设置为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
。
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;
/
答案 2 :(得分:0)
代码的基本问题是您创建了一个名为ROWNUM的变量,并最初将其赋值为0。因此,当您第一次运行查询时,它将始终通过
的条件 WHERE ROWNUM <= 10000
,您的查询将根据其他条件更新所有符合条件的记录。 因此,在当前情况下,由于您的记录超过10k,因此查询将仅运行一次。 如果记录少于1万条,则您的程序将进入 INFINITE循环。
您唯一需要做的更改就是从代码中删除以下 ROWNUM 声明,您的代码将按预期工作。
ROWNUM NUMBER := 0;
然后该程序将使用 Oracle的内置ROWNUM 子句。