代码之家  ›  专栏  ›  技术社区  ›  m_pGladiator

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

  •  32
  • m_pGladiator  · 技术社区  · 14 年前

    我有一个有很多记录的表(可能超过500000或1000000)。我在这个表中添加了一个新列,我需要使用这个表中另一列的对应行值为列中的每一行填充一个值。

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

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

    6 回复  |  直到 14 年前
        1
  •  61
  •   Marcelo Cantos    14 年前

    通常的方法是使用更新:

    UPDATE mytable
       SET new_column = <expr containing old_column>
    

    你应该能够做到这是一个单一的交易。

        2
  •  8
  •   Jeffrey Kemp    6 年前

    正如马塞洛所说:

    UPDATE mytable
    SET new_column = <expr containing old_column>;
    

    如果由于“snapshot too old”(快照太旧)错误(例如,如果表达式查询另一个高度活动的表),并且如果列的新值始终不为NULL,则可以批量更新表:

    UPDATE mytable
    SET new_column = <expr containing old_column>
    WHERE new_column IS NULL
    AND ROWNUM <= 100000;
    

    只要运行这个语句,提交,然后再次运行它;冲洗,重复,直到报告“0行更新”。这将花费更长的时间,但每次更新都不太可能失败。

    一个更有效的替代方法是使用 DBMS_PARALLEL_EXECUTE 应用程序编程接口。

    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

        3
  •  2
  •   Timothy    14 年前

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

        4
  •  0
  •   David    14 年前

    可能对你不管用,但我过去也曾在类似情况下使用过几次这种技术。

    创建更新的{table\u name},然后选择insert into this table in batches。一旦完成,这就取决于Oracle(我不知道也不使用)支持以原子方式重命名表的能力。更新后的{table\u name}变为{table\u name},而{table\u name}变为原始的{table\u name}。

        5
  •  0
  •   Stellios    14 年前
        6
  •  0
  •   user3098137    6 年前

    更新Hotelid>=30的酒店设置折扣1和Hotelid<=5504

        7
  •  0
  •   anaconda    3 年前

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