1

作为更大处理项目的一部分,我正在尝试找到一种更好的方法将数据从表中提取到另一个表中。我认为我可以通过 BULK COLLECT 和 FORALL 来完成,并获得显着的速度,但我认为我不能使用 BULK COLLECT 处理单个列引用......

我有一个关于继承的数据/应用程序迁移项目(MSSQL 到 Oracle 11.2)。我正在尝试优化和端到端检查...该过程的第一步是将遗留数据(数据库表,4.5M 记录,170 列,全部为字符串格式)导入另一个表。

最初的转换是基于游标的,逐行循环,每一列都经过至少一个用于清除/转换的函数。它有效,但在测试系统上花费了太长时间——用非常简单的函数将 450 万条记录从一个表转换到另一个表需要超过 12 个小时。在我可以访问的本地实现中,它们最终限制为 13000 个单元 ID 号 ID 超过 220k 记录。

我在我的笔记本电脑上设置了一个更有限的开发系统来测试替代技术——并且可以获得超过 5 倍的导入速度,但这仍然是光标/行。我已将表设置为 NOLOGGING 并使用 APPEND 提示。我已经测试了有/没有索引。我不能用那个尺寸的桌子做 SELECT INTO —— 它只是窒息。

还有另一种/更好的技术吗?我还能如何提高转换速度?我用 BULK COLLECT 做错了吗(即有没有办法引用各个字段?)

如果有人有任何见解,请插播!我包含了一个非常精简的过程版本,所以我可以展示我的使用尝试。同样的事情(几乎)作为常规游标循环运行,只是不使用 FORALL 和 (i) 下标。我得到的错误是 ORA-00913: Too Many Values。我已经完成了完整的插入语句,将字段与值匹配。我检查了数据转换函数——它们适用于常规列作为参数。我想知道他们是否因为下标而无法使用 BULK COLLECT 和/或 FORALL ?

更新信息: 这是在一个访问受限的系统上,直到现在(等待帐户),我一直不得不通过在本地系统上运行来远程诊断“真实”(客户)DEV系统——分析代码,数据,时间等。我的建议是由另一位开发人员提出的,他会反馈我的结果。严重地。但是...... @Mark,@Justin - 通常情况下,我会摆脱任何游标而不是?绝对?需要,并尽可能使用 SELECT INTO。这通常是我对旧 PL/SQL 代码的第一个建议......(“为什么。所以。光标?”穿着小丑化妆)。这是我在本地系统上尝试的第一件事,但它只是让服务器慢下来,我退出了测试。那是在减少 NOLOGGING 实施之前 - 这就是我
在查看了时间、查询、连接、索引和哭泣之后,我推荐了 NOLOGGING 并转换为 INSERT /*+ APPEND */ -- 这在其他进程中赢得了时间,主要是由连接构建的表。

回复:“OID <= '000052000'” - 当他们在 cust 开发系统上设置第一次转换代码时,他们必须限制从 PMS_OHF 表转换的记录数量。最初,他们可以在合理的时间内处理 13000 个人员标识符。这 13000 个 ID 将在大约 220K 记录中,所以,当我加入时,这就是他们正在移动的内容。一些重写、加入更正和 NOLOGGING/Insert Append 产生了足够大的差异,他们继续进行。在本地系统上,我认为 13000 太小了——我认为我无法与遗留结果进行有意义的比较——所以我提高了它,并提高了它。我应该勇敢地尝试在笔记本电脑开发系统上进行全面转换——在这里我至少可以通过 EM 观察正在发生的事情......政府不会 不允许他们的 DBA 使用它。(!?)

更大的信息:-- 在再次思考 00913 错误并回想其他项目之后,我意识到早期的错误是当多个元素被传递给一个期望单个元素的函数时......这让我回到了我试图使用下标BULK COLLECT 循环中的字段名称。我重新观看了几个 Steven Feuerstein YT 的演讲,我认为它终于融入了。简单的网络示例......我正在水平制作我的类型,而不是垂直(反之亦然)......为了得到我的函数调用才能工作,我想我必须为每个字段创建一个类型,以及该类型的数组/表。突然(170 次)我想我会看一些关于手动并行的 Tom Kyte 课程,然后问 wx 我可以访问新的(11.2?)DBMS_PARALLEL_EXECUTE 接口——我对此表示怀疑。还,不知道更多关于 cust 开发系统的信息,除了最好称为“不充分”的描述,我不知道 wx //ism 会是一个巨大的帮助。我需要阅读 //ism

我所知道的是,我必须完成一些完整的运行,否则我会不愿意说我们的结果“足够接近”遗留结果。对于我们的测试,我们可能没有太多选择来进行多天的完整运行。

      PROCEDURE CONVERT_FA IS    

    CURSOR L_OHF IS   -- Cursor used to get SOURCE TABLE data
        SELECT * 
        FROM TEST.PMS_OHF -- OHF is legacy data source
        where  OID <= '000052000'   -- limits OHF data to a smaller subset
        ORDER BY ID ;

    L_OHF_DATA TEST.PMS_OHF%ROWTYPE;
    L_SHDATA TEST.OPTM_SHIST%ROWTYPE;

    Type hist_Array is table of TEST.PMS_OHF%ROWTYPE;
    SHF_INPUT hist_array ; 



    Type Ohist_Array is table of TEST.OPTM_SHIST%ROWTYPE;
    TARG_SHIST ohist_Array ;

    n_limit number := 1000 ;    

  BEGIN

    begin

      OPEN L_OHF;

      LOOP 
        FETCH L_OHF BULK COLLECT INTO SHF_INPUT LIMIT n_limit ;
        FORALL i in 1 .. n_limit
          INSERT INTO TEST.OPTM_SHIST
      (  -- There are 170 columns in target table, requiring diff't xformations
              RECORD_NUMBER , UNIQUE_ID , STRENGTH_YEAR_MONTH , FY , FM , ETHNIC , 
              SOURCE_CODE_CURR , SOURCE_CODE_CURR_STAT , 
                -- ... a LOT more fields
              DESG_DT_01 ,  
                -- and some place holders for later
              SOURCE_CALC , PSID ,  GAIN_CURR_DT_CALC 
      )
      values
      ( -- examples of xformatiosn
            SHF_INPUT.ID(i) ,
            '00000000000000000000000' || SHF_INPUT.IOD(i) ,
            TEST.PMS_UTIL.STR_TO_YM_DATE( SHF_INPUT.STRYRMO(i) ) ,
            TEST.PMS_UTIL.STR_TO_YEAR( SHF_INPUT.STRYRMO(i) ) ,
            TEST.PMS_UTIL.STR_TO_MONTH( SHF_INPUT.STRYRMO(i) ) ,
            TEST.PMS_UTIL.REMOVE_NONASCII( SHF_INPUT.ETHNIC(i) ) ,
            -- ... there are a lot of columns
            TEST.PMS_UTIL.REMOVE_NONASCII( SUBSTR( SHF_INPUT.SCCURPRICL(i),1,2 ) ) ,
            TEST.PMS_UTIL.REMOVE_NONASCII( SUBSTR( SHF_INPUT.SCCURPRICL(i),3,1 ) ) ,   

            -- an example of other transformations
            ( case 
                when ( 
                      ( 
                       SHF_INPUT.STRYRMO(i) >= '09801' 
                       AND 
                       SHF_INPUT.STRYRMO(i) < '10900' 
                      )  
                    OR 
                     ( 
                      SHF_INPUT.STRYRMO(i) = '10901' 
                      AND 
                      SHF_INPUT.DESCHGCT01(i) = '081' 
                      ) 
                    ) 

                then   TEST.PMS_UTIL.STR_TO_DATE( SHF_INPUT.DESCHGCT01(i) || SHF_INPUT.DESCHGST01(i) )  

                else  TEST.PMS_UTIL.STR_TO_DATE( SHF_INPUT.DESCHGDT01(i) ) 
             end ),

            -- below are fields that will be filled later
            null ,  -- SOURCE_CALC ,
            SHF_INPUT.OID(i) ,
            null   -- GAIN_CURR_DT_CALC 
           )  ;

        EXIT WHEN L_OHF%NOTFOUND; -- exit when last row is fetched

      END LOOP;

      COMMIT;

      close L_OHF;

    END;
  end CONVERT_OHF_FA;
4

2 回答 2

0
execute immediate 'alter session enable parallel dml';
INSERT /*+ APPEND PARALLEL */ INTO TEST.OPTM_SHIST(...)
SELECT ...
FROM TEST.PMS_OHF
WHER OID <= '000052000';

这是进行大数据加载的方法。不要被所有花哨的 PL/SQL 选项(如批量收集、流水线表等)所迷惑。它们很少比普通的旧 SQL 更快或更容易使用。这些功能的主要好处是在不进行重大重构的情况下提高逐行处理过程的性能。

在这种情况下,看起来 PL/SQL 中实际上已经没有逻辑了。几乎所有的 PL/SQL 都可以被丢弃并用单个查询替换。这使得修改、调试、添加并行性等变得更加容易。

其他一些提示:

  1. ORDER BY可能对数据加载没有帮助。除非您尝试对索引做一些花哨的事情,例如提高聚类因子或在不排序的情况下重建。
  2. 如果相同输入的输出始终相同,请确保将您的函数声明为 DETERMINISTIC。这可能有助于 Oracle 避免为相同的结果调用该函数。为了获得更好的性能,您可以内联 SQL 语句中的所有函数,但这可能会变得混乱。
  3. 如果还需要使用BULK COLLECT,就使用提示APPEND_VALUES,不要APPEND
于 2014-03-28T04:57:18.797 回答
0

在为其他问题放弃这个之后,我今天再次选择了这个。

有人给我发了他们类似代码的片段,我决定坐下来,蛮力解决这个问题:转到最小的列数和匹配值,并增加列/值并重新编译......

然后它击中了我……我的索引放错了地方。

不正确的形式:

    SHF_INPUT.ID(i) ,
    '00000000000000000000000' || SHF_INPUT.IOD(i) ,
    TEST.PMS_UTIL.STR_TO_YM_DATE( SHF_INPUT.STRYRMO(i) ) ,

正确形式:

    SHF_INPUT(i).ID ,
    '00000000000000000000000' || SHF_Input(i).IOD ,
    TEST.PMS_UTIL.STR_TO_YM_DATE( SHF_Input(i).STRYRMO ) ,

我将其归咎于查看早期的多列批量收集示例,并假设我可以将它们转换为 %ROWTYPE 示例。我很不耐烦,没有检查。

感谢您的帮助和建议。

于 2014-04-05T03:00:28.607 回答