繁体   English   中英

使用Rowid从一张表更新百万行到另一张Oracle

[英]Update million rows using rowids from one table to another Oracle

嗨,我有两个表,每个表都有一百万行。我有oracle 11 g R1,我敢肯定我们中的许多人肯定已经经历了这种情况。

什么是从一个表更新到值不同的另一个表的最有效,最快捷的方法。

例如:表1具有4个高精度的NUMBER列,例如:0.2212454215454212

表2有6列。 根据两个表上的公共列更新表2的四个列,仅更新不同的列。

我有这样的东西

DECLARE
TYPE test1_t IS TABLE OF test.score%TYPE INDEX BY PLS_..;
TYPE test2_t IS TABLE OF test.id%TYPE INDEX BY PLS..; 
TYPE test3_t IS TABLE OF test.Crank%TYPE INDEX BY PLS..;

vscore test1_t;
vid test2_t;
vurank test4_t;

BEGIN
  SELECT id,score,urank
    BULK COLLECT INTO vid,vscore,vurank
    FROM test;

  FORALL i IN 1 .. vid.COUNT
    MERGE INTO final T
      USING (SELECT vid (i) AS o_id,
                    vurank (i) AS o_urank,
                    vscore (i) AS o_score FROM DUAL) S
      ON (S.o_id = T.id)
    WHEN MATCHED THEN
      UPDATE SET T.crank = S.o_crank
      WHERE T.crank <> S.o_crank;

由于数字的精度很高,它会变慢吗?

如果必须更新100万行,在最坏的情况下,我仍然尝试使用“批量收集和合并”组合花费大约30分钟的时间。

有rowid吗? 帮助将不胜感激。

如果要更新所有行,则只需使用update:

update table_1
set    (col1,
        col2) = (
         select col1,
                col2
         from   table2
         where  table2.col_a = table1.col_a and
                table2.col_b = table1.col_b)

批量收集或任何PL / SQL技术总是比纯SQL技术要慢。

数值精度可能并不重要,并且rowid不相关,因为两个表之间没有通用值。

当处理数百万行时,并行DML会改变游戏规则。 当然,您需要具有企业版才能使用并行,但这实际上是唯一会带来很大变化的东西。

我建议您阅读rleishman比较8 Bulk Update Methods的有关OraFAQ的文章。 他的主要发现是“到目前为止,磁盘读取的成本远远超过了上下文切换,因此几乎看不到(原文如此)”。 换句话说,除非您的数据已经缓存在内存中,否则SQL和PL / SQL方法之间确实没有显着差异。

本文确实对并行应用提出了一些巧妙的建议。 令人惊讶的结果是并行流水线功能可提供最佳性能。

专注于已使用的语法并跳过逻辑 (可能使用纯更新+纯插入可能会解决问题,合并成本,索引,可能对合并进行全面扫描等)
您应该在Bulk Collect语法中使用Limit
无限制使用批量收集

  1. 将所有记录都装入内存
  2. 没有部分提交的合并,您将创建一个大的重做日志,该重做日志必须在流程结束时应用。

两者都会导致性能降低。

DECLARE
 v_fetchSize NUMBER := 1000; -- based on hardware, design and .... could be scaled
 CURSOR a_cur IS 
 SELECT id,score,urank FROM test;    
 TYPE myarray IS TABLE OF a_cur%ROWTYPE;
 cur_array myarray;

    BEGIN
      OPEN a_cur;
      LOOP
        FETCH a_cur BULK COLLECT INTO cur_array LIMIT v_fetchSize;
          FORALL i IN 1 .. cur_array.COUNT
          // DO Operation
          COMMIT;
        EXIT WHEN a_cur%NOTFOUND;
      END LOOP;
      CLOSE a_cur;
    END;
  1. 只是要确保:必须对test.idfinal.id进行索引。

  2. 第一次select ... from test您从Table 1获得了太多记录,然后需要将所有这些与Table 2记录进行比较。 尝试仅选择您需要更新的内容。 因此,至少有2个变体:

a)仅选择更改的记录:

  SELECT source_table.id, source_table.score, source_table.urank 
  BULK COLLECT INTO vid,vscore,vurank 
  FROM 
    test source_table, 
    final destination_table
  where 
    source_table.id = destination_table.id 
    and
    source_table.crank <> destination_table.crank
   ;

b)使用日期时间值将新字段添加到源表中,并在触发器中填充当前时间。 在同步选择时,仅记录在过去一天中发生了更改。 该字段需要索引。

在更新阶段进行了此类更改之后,您无需比较其他字段,只需匹配ID:

  FORALL i IN 1 .. vid.COUNT 
  MERGE INTO FINAL T 
  USING (
    SELECT vid (i) AS o_id,
           vurank (i) AS o_urank,
           vscore (i) AS o_score FROM DUAL
  ) S 
  ON (S.o_id = T.id) 
  WHEN MATCHED 
  THEN UPDATE SET T.crank = S.o_crank 

如果您担心撤消/重做段的大小,则变体b)更为有用,因为您可以从源Table 1获取记录并划分为时间片,并在更新每个片之后提交更改。 例如,从00:00到01:00,从01:00到02:00等。在此变体中,只需通过SQL语句即可完成更新,而不必在保持可接受的重做/撤消日志大小的情况下,将数据选择到行的集合中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM