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

m_p*_*tor 34 sql oracle performance

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

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

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

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

Mar*_*tos 53

通常的方法是使用UPDATE:

UPDATE mytable
   SET new_column = <expr containing old_column>
Run Code Online (Sandbox Code Playgroud)

您应该能够做到这一点是一个单一的交易.

  • 听起来OP知道如何在单个事务中执行此操作,但是存在性能问题,因此他尝试将其批处理为单独的事务。 (2认同)
  • 这是可能的,但非常特殊的是,一百万行需要很长时间才能更新单个列。由于缺乏对集合操作的理解,或者由于他们试图在客户端代码中计算新的值(出于不必要或由于缺乏理解而又一次),OP也可能一次更新一条记录。 。无论哪种情况,如果OP指出适用于上述情况的哪种情况,我都可以更新我的答案。 (2认同)
  • OP:如果这是性能问题,请在安静的时间进行。如果您的DBMS无法处理一百万行的更新,那么是时候开始寻找新的DBMS了:-) (2认同)

Jef*_*emp 8

正如马塞洛所说:

UPDATE mytable
SET new_column = <expr containing old_column>;
Run Code Online (Sandbox Code Playgroud)

如果由于"快照太旧"错误而花费太长时间并且失败(例如,如果表达式查询另一个高活动表),并且列的新值始终为NOT NULL,则可以批量更新表:

UPDATE mytable
SET new_column = <expr containing old_column>
WHERE new_column IS NULL
AND ROWNUM <= 100000;
Run Code Online (Sandbox Code Playgroud)

只需运行此语句COMMIT,然后再次运行它; 冲洗,重复直到报告"0行更新".它需要更长的时间,但每次更新都不太可能失败.

编辑:

更有效的替代方案是使用DBMS_PARALLEL_EXECUTEAPI.

示例代码(来自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;
/
Run Code Online (Sandbox Code Playgroud)

Oracle Docs:https://docs.oracle.com/database/121/ARPLS/d_parallel_ex.htm#ARPLS67333