将 oracle DB 中的 100 万条记录更新为 10k 批次;
updating 1 million records in oracle DB as batch of 10k;
我必须将 oracle 数据库中的 table 更新为 10k 批次。
我试过这个:
BEGIN
WHILE (true) LOOP
UPDATE TOP (10000) CUSTOMERS SET ACTIVE = 'N' WHERE ACTIVE='Y';
IF sql%notfound THEN
EXIT;
END IF;
COMMIT;
END LOOP;
END;
它不起作用,因为 plsql 不支持 top。
有什么建议吗?
如果您在 CUSTOMERS
table 上有自动递增的合成 ID
列,那么您可以简单地使用 WHERE
条件说
WHERE ID <= 10000
UPDATE CUSTOMERS
SET ACTIVE = 'N'
WHERE ACTIVE='Y'
AND ROWNUM <= 10000; -- first 10k rows
在 jva 的 答案之上添加以处理所有行而不更改每个批次的查询:
更新前:
select count(1) from cust ; --1000000
select distinct active from cust ; -- Y
更新:
Begin
for i in 1..100
loop
update cust set Active = 'N' where ACTIVE = 'Y'
and rownum <= 10000;
-- dbms_output.put_line ('i value : ' || i );
commit;
end loop;
dbms_output.put_line ('All rows updated ' );
end;
输出:
All rows updated
Statement processed.
3.77 seconds
之后:
select distinct active from cust ; -- N
你的 pl/SQL 块,使用 JVA 的建议,应该像这样完成(因为你是新手 pl/sql 我正在添加一些你可能感兴趣的语法建议):
BEGIN
-- WHILE (TRUE) can be omitted: "loop ... end loop;"
-- already is an endless loop
LOOP
UPDATE CUSTOMERS
SET ACTIVE = 'N'
WHERE ACTIVE='Y'
AND rownum <= 1000;
exit when sql%notfound; -- notice that exit accepts "when condition"
--IF sql%notfound THEN -- you can avoid a if/endif by using "exit when"
-- EXIT;
-- END IF;
COMMIT;
END LOOP;
commit; -- you missed this commit for the last iteration
END;
不要试图在“sql%notfound 时退出”之前放置“commit”:在“commit”之后 sql%notfound 始终为 false,您的循环将真的没完没了
我要指出的是,为了提高效率,这种方法需要对“ACTIVE”列进行索引!
如果您在“活动”列上没有索引,则每次“更新”都将被迫从头开始进行完整的 table 扫描,只是为了找到下 1000 条仍需要的记录待更新。
我提议的另一种方法使用了一些高级 PL/SQL 功能,作为学习者,您可能会感兴趣(rowid、“table of”、cursor bulk fetchs 和“forall”)并且只扫描一次 table 进行更新,因此(在没有索引的情况下)它比以前的方法执行得更好。
请记住,如果您有索引,这会更慢 (但使用 foralls、批量收集和 rowid 访问,它并没有那么慢),但在某些情况下它会很方便更复杂(例如:当 where 条件需要使用无法提高速度的复杂连接从其他 table 访问数据时)。在某些情况下,“where”是如此复杂和缓慢,以至于您真的不想使用“where rownum<=1000”方法一遍又一遍地重新执行它。
declare
type rowid_array is table of rowid;
ids rowid_array;
cursor cur is
select rowid as id
from CUSTOMERS
where ACTIVE='Y';
begin
open cur;
loop
fetch cur bulk collect into ids limit 1000;
exit when ids.count = 0;
forall c in ids.first .. ids.last
update CUSTOMERS set ACTIVE='N'
where rowid = ids(c);
commit;
end loop;
end;
我必须将 oracle 数据库中的 table 更新为 10k 批次。
我试过这个:
BEGIN
WHILE (true) LOOP
UPDATE TOP (10000) CUSTOMERS SET ACTIVE = 'N' WHERE ACTIVE='Y';
IF sql%notfound THEN
EXIT;
END IF;
COMMIT;
END LOOP;
END;
它不起作用,因为 plsql 不支持 top。
有什么建议吗?
如果您在 CUSTOMERS
table 上有自动递增的合成 ID
列,那么您可以简单地使用 WHERE
条件说
WHERE ID <= 10000
UPDATE CUSTOMERS
SET ACTIVE = 'N'
WHERE ACTIVE='Y'
AND ROWNUM <= 10000; -- first 10k rows
在 jva 的 答案之上添加以处理所有行而不更改每个批次的查询:
更新前:
select count(1) from cust ; --1000000
select distinct active from cust ; -- Y
更新:
Begin
for i in 1..100
loop
update cust set Active = 'N' where ACTIVE = 'Y'
and rownum <= 10000;
-- dbms_output.put_line ('i value : ' || i );
commit;
end loop;
dbms_output.put_line ('All rows updated ' );
end;
输出:
All rows updated
Statement processed.
3.77 seconds
之后:
select distinct active from cust ; -- N
你的 pl/SQL 块,使用 JVA 的建议,应该像这样完成(因为你是新手 pl/sql 我正在添加一些你可能感兴趣的语法建议):
BEGIN
-- WHILE (TRUE) can be omitted: "loop ... end loop;"
-- already is an endless loop
LOOP
UPDATE CUSTOMERS
SET ACTIVE = 'N'
WHERE ACTIVE='Y'
AND rownum <= 1000;
exit when sql%notfound; -- notice that exit accepts "when condition"
--IF sql%notfound THEN -- you can avoid a if/endif by using "exit when"
-- EXIT;
-- END IF;
COMMIT;
END LOOP;
commit; -- you missed this commit for the last iteration
END;
不要试图在“sql%notfound 时退出”之前放置“commit”:在“commit”之后 sql%notfound 始终为 false,您的循环将真的没完没了
我要指出的是,为了提高效率,这种方法需要对“ACTIVE”列进行索引!
如果您在“活动”列上没有索引,则每次“更新”都将被迫从头开始进行完整的 table 扫描,只是为了找到下 1000 条仍需要的记录待更新。
我提议的另一种方法使用了一些高级 PL/SQL 功能,作为学习者,您可能会感兴趣(rowid、“table of”、cursor bulk fetchs 和“forall”)并且只扫描一次 table 进行更新,因此(在没有索引的情况下)它比以前的方法执行得更好。 请记住,如果您有索引,这会更慢 (但使用 foralls、批量收集和 rowid 访问,它并没有那么慢),但在某些情况下它会很方便更复杂(例如:当 where 条件需要使用无法提高速度的复杂连接从其他 table 访问数据时)。在某些情况下,“where”是如此复杂和缓慢,以至于您真的不想使用“where rownum<=1000”方法一遍又一遍地重新执行它。
declare
type rowid_array is table of rowid;
ids rowid_array;
cursor cur is
select rowid as id
from CUSTOMERS
where ACTIVE='Y';
begin
open cur;
loop
fetch cur bulk collect into ids limit 1000;
exit when ids.count = 0;
forall c in ids.first .. ids.last
update CUSTOMERS set ACTIVE='N'
where rowid = ids(c);
commit;
end loop;
end;