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

pbpqsu0x  于 2023-02-18  发布在  Oracle
关注(0)|答案(7)|浏览(160)

我有一个包含大量记录的表(可能超过500 000或1 000 000)。我在此表中添加了一个新列,需要使用此表中另一列的相应行值为该列中的每一行填充一个值。
我尝试使用单独的事务来选择下一个100条记录的块并更新它们的值,但例如在Oracle10中更新所有记录仍然需要几个小时。
什么是最有效的方法来做到这一点,在SQL,而不使用一些方言特定的功能,使它的工作无处不在(Oracle,MSSQL,MySQL,PostGre等)?
其他信息:没有计算字段。有索引。使用生成的SQL语句逐行更新表。

66bbxpm5

66bbxpm51#

通常的方法是使用UPDATE:

UPDATE mytable
   SET new_column = <expr containing old_column>

你应该可以做到这是一个单一的交易。

91zkwejq

91zkwejq2#

正如马塞洛所说:

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文档:www.example.comhttps://docs.oracle.com/database/121/ARPLS/d_parallel_ex.htm#ARPLS67333

rt4zxlrg

rt4zxlrg3#

您可以删除表上的任何索引,然后执行插入,再重新创建索引。

w6lpcovy

w6lpcovy4#

可能对你不起作用,但这是我过去在类似情况下用过几次的技巧。
创建了updated_{table_name},然后选择批量插入到此表中。完成后,这取决于Oracle(我不知道也不使用)是否支持以原子方式重命名表的功能。updated_{table_name}将变为{table_name},而{table_name}将变为original_{table_name}。
上一次我不得不这样做是为了一个有几百万行的索引很重的表,在对它进行一些重大更改所需的持续时间内,绝对不能锁定它。

wydwbb8l

wydwbb8l6#

更新酒店设置折扣=30,其中酒店ID〉= 1且酒店ID〈= 5504

xuo3flqw

xuo3flqw7#

对于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;

更新:

  • 一个改进是,如果您的表非常大,您将不会锁定该表,在这种情况下,此操作可能需要几分钟。
  • 仅当您确定在此过程中不发生插入和/或更新时。

相关问题