1

嗨,我有两张表,每张都有百万行。我有 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有关的吗?帮助将不胜感激。

4

4 回答 4

2

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

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 不相关,因为两个表之间没有共同值。

于 2013-06-17T20:31:59.483 回答
2

在处理数百万行时,并行 DML 可以改变游戏规则。当然你需要有企业版才能使用并行,但它确实是唯一会产生很大差异的东西。

我建议您阅读 rleishman 在 OraFAQ 上比较8 种批量更新方法的文章。他的主要发现是“到目前为止,磁盘读取的成本超过了上下文切换,以至于它们几乎不引人注意(原文如此)”。换句话说,除非您的数据已经缓存在内存中,否则 SQL 和 PL/SQL 方法之间确实没有显着差异。

这篇文章确实有一些关于使用并行的简洁建议。令人惊讶的结果是并行流水线函数提供了最佳性能。

于 2013-06-18T11:59:46.623 回答
1

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

  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;
于 2013-06-18T11:55:45.293 回答
0
  1. 只是为了确定:test.id并且final.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 语句完成更新,而无需将数据连续选择到集合中,同时保持可接受的重做/撤消日志大小。

于 2013-06-17T23:04:22.810 回答