我有一张包含大量记录的表格(可能超过500 000或1 000 000)。我在此表中添加了一个新列,我需要使用此表中另一列的相应行值为列中的每一行填充一个值。
我尝试使用单独的事务来选择100个记录的每个下一个块并更新它们的值,但是这仍需要几个小时才能更新Oracle10中的所有记录。
在SQL中执行此操作的最有效方法是什么,不使用某些特定于方言的功能,因此它可以在任何地方使用(Oracle,MSSQL,MySQL,PostGre等)?
附加信息:没有计算字段。有索引。使用生成的SQL语句逐行更新表。
答案 0 :(得分:51)
通常的方法是使用UPDATE:
UPDATE mytable
SET new_column = <expr containing old_column>
你应该可以做到这一点是单一的交易。
答案 1 :(得分:8)
正如马塞洛所说:
UPDATE mytable
SET new_column = <expr containing old_column>;
如果由于“快照太旧”错误而导致时间过长而失败(例如,如果表达式查询另一个高活动表),并且列的新值始终为NOT NULL,则可以更新表中的批次:
UPDATE mytable
SET new_column = <expr containing old_column>
WHERE new_column IS NULL
AND ROWNUM <= 100000;
只需运行此语句COMMIT,然后再次运行它;冲洗,重复直到报告“0行更新”。它需要更长的时间,但每次更新都不太可能失败。
修改强>
更有效的替代方案是使用DBMS_PARALLEL_EXECUTE
API。
示例代码(来自Oracle文档):
DECLARE
l_sql_stmt VARCHAR2(1000);
l_try NUMBER;
l_status NUMBER;
BEGIN
-- Create the TASK
DBMS_PARALLEL_EXECUTE.CREATE_TASK ('mytask');
-- Chunk the table by ROWID
DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_ROWID('mytask', 'HR', 'EMPLOYEES', true, 100);
-- Execute the DML in parallel
l_sql_stmt := 'update EMPLOYEES e
SET e.salary = e.salary + 10
WHERE rowid BETWEEN :start_id AND :end_id';
DBMS_PARALLEL_EXECUTE.RUN_TASK('mytask', l_sql_stmt, DBMS_SQL.NATIVE,
parallel_level => 10);
-- If there is an error, RESUME it for at most 2 times.
l_try := 0;
l_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS('mytask');
WHILE(l_try < 2 and l_status != DBMS_PARALLEL_EXECUTE.FINISHED)
LOOP
l_try := l_try + 1;
DBMS_PARALLEL_EXECUTE.RESUME_TASK('mytask');
l_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS('mytask');
END LOOP;
-- Done with processing; drop the task
DBMS_PARALLEL_EXECUTE.DROP_TASK('mytask');
END;
/
Oracle文档:https://docs.oracle.com/database/121/ARPLS/d_parallel_ex.htm#ARPLS67333
答案 2 :(得分:2)
您可以删除表上的任何索引,然后执行插入操作,然后重新创建索引。
答案 3 :(得分:0)
可能不适合你,但我过去曾经使用过几次类似情况的技术。
创建updated_ {table_name},然后选择批量插入此表。一旦完成,这取决于Oracle(我不知道或使用)支持以原子方式重命名表的能力。 updated_ {table_name}变为{table_name},而{table_name}变为original_ {table_name}。
上次我不得不这样做是为了一个包含数百万行的重度索引表,在对其进行一些严重更改所需的持续时间内绝对无法锁定。
答案 4 :(得分:0)
什么是数据库版本?查看11g中的虚拟列:
答案 5 :(得分:0)
更新酒店设置Discount = 30,其中Hotelid> = 1和Hotelid <= 5504
答案 6 :(得分:0)
对于 Postgresql 我做这样的事情(如果我们确定不再发生更新/插入):
create table new_table as table orig_table with data;
update new_table set column = <expr>
start transaction;
drop table orig_table;
rename new_table to orig_table;
commit;