Oracle数据转换 - Bulk / ForALL不起作用

时间:2014-03-27 21:44:53

标签: oracle query-optimization etl bulk-collect

我试图找到一种更好的方法,将数据从桌面拉到另一个,作为更大的处理项目的一部分。我以为我可以通过BULK COLLECT和FORALL来实现,并且速度很快,但我认为我不能使用BULK COLLECT处理单个列引用...

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

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

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

还有其他/更好的技巧吗?我怎样才能获得转换速度?我是否错误地使用BULK COLLECT(即有没有办法引用各个字段?)

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

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

重新:" OID< =' 000052000' " - 当他们在cust dev系统上设置第一个转换代码时,他们必须限制他们从PMS_OHF表转换的记录数量。最初,他们可以在合理的时间内获得13000个人员标识符。那些13000个ID将在大约220K记录中,因此,当我加入时它们正在移动。一些重写,加入更正,NOLOGGING /插入附加产生了足够大的差异,他们继续。在本地系统上,我认为13000太小了 - 我不认为我对遗留结果进行了有意义的比较 - 所以我把它加了起来,然后加了它。我应该勇敢地尝试在笔记本电脑开发系统上进行全面转换 - 在这里,我至少可以观察通过EM进行的事情......政府不允许他们的DBA使用它。 (!?)

更大的信息: - 在再次思考00913错误并回想其他项目后,我意识到之前的错误是当多个元素传递给期望单个元素的函数时...指向我试图在BULK COLLECT循环中使用下标字段名称。我重新观看了几个Steven Feuerstein YT演示文稿,我认为它最终沉入其中。简单的网络示例......我正在横向制作我的类型,而不是垂直(或反之亦然)...为了得到我的函数调用工作,我想我必须为每个字段创建一个TYPE,以及该TYPE的ARRAY / TABLE。突然(170次)我想我会看一些关于手动并行的Tom Kyte课程,并问wx我是否可以访问新的(11.2?)DBMS_PARALLEL_EXECUTE接口 - 我对此表示怀疑。此外,不了解更多关于cust dev系统的信息,除了最好的描述"不足之外,我不知道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;

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

在解决其他问题之后,我今天再次选择了这个。

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

然后它击中了我...我的索引在错误的地方。

INCORRECT表格:

    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示例。我很不耐烦,没有检查。

感谢您的帮助和建议。