更新表中所有行的有效方法

时间:2010-04-14 07:45:34

标签: sql performance oracle

我有一张包含大量记录的表格(可能超过500 000或1 000 000)。我在此表中添加了一个新列,我需要使用此表中另一列的相应行值为列中的每一行填充一个值。

我尝试使用单独的事务来选择100个记录的每个下一个块并更新它们的值,但是这仍需要几个小时才能更新Oracle10中的所有记录。

在SQL中执行此操作的最有效方法是什么,不使用某些特定于方言的功能,因此它可以在任何地方使用(Oracle,MSSQL,MySQL,PostGre等)?

附加信息:没有计算字段。有索引。使用生成的SQL语句逐行更新表。

7 个答案:

答案 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中的虚拟列:

添加具有默认值的列 http://www.oracle.com/technology/pub/articles/oracle-database-11g-top-features/11g-schemamanagement.html

答案 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;