查询优化 - Oracle 数据转换 - 批量/ForALL 不起作用



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

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

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

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

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

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

更新信息:这是在一个受限访问系统上,到目前为止(等待帐户),我一直不得不通过在本地系统上运行来远程诊断"真实"(客户)DEV系统——分析代码、数据、时间等。我的建议是由另一位开发人员提出的,他会向我反馈结果。认真地然而@Mark,@Justin-通常情况下,我会去掉任何光标,不是吗?绝对地需要,并在可能的情况下使用SELECT INTO。这通常是我对旧PL/SQL代码的第一个建议。。。("为什么。那么。光标?"化着小丑的妆)。这是我在本地系统上尝试的第一件事,但它只是把服务器拖慢了,我退出了测试。那是在减少的NOLOGING实现之前——当我可以触摸开发系统时,我会尝试这样做
在查看了计时、查询、联接、索引和哭泣之后,我建议NOLOGING并转换为INSERT/*+APPEND*/——这为其他进程(主要是基于联接构建的表)争取了时间。

re:"OID<='000052000'"-当他们在客户开发系统上设置第一个转换的代码时,他们必须限制从PMS_OHF表转换的记录数量。最初,他们可以在合理的时间内获得13000个人员标识符进行处理。这13000个身份证将在大约22万份记录中,所以,当我上船时,他们就是这么搬进来的。一些重写、连接更正和NOLOGING/Insert-Append产生了足够大的差异,它们继续进行。在本地系统上,我认为13000太小了——我认为我无法与遗留结果进行有意义的比较——所以我提高了它,并提高了它。我应该勇敢地尝试在笔记本电脑开发系统上进行完全转换——在这里,我至少可以通过EM了解情况……政府不允许他们的DBA使用它。(!?)

更大的信息:——在再次思考00913错误并回顾其他项目后,我意识到早期的错误是将多个元素传递给期望单个元素的函数。。。这让我回到了在BULK COLLECT循环中尝试使用下标字段名的问题。我重新看了几次Steven Feuerstein YT的演示,我想它终于深入人心了。。。我是水平地打字,而不是垂直地打字(反之亦然)。。。为了让我的函数调用正常工作,我想我必须为每个字段创建一个TYPE,并创建一个该TYPE的ARRAY/TABLE。突然(170次),我想我会看一些Tom Kyte关于手动并行的课程,并问wx我是否可以访问新的(11.2?)DBMS_PARALLE_EXECUTE接口——我对此表示怀疑。此外,我不知道更多关于客户开发系统的信息,除了最好称之为"不充分"的描述之外,我不认为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;
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

在为其他问题删除了这个之后,我今天又重新拿起了这个。

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

然后我突然想到…我的索引放错了地方。

错误形式:

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

感谢您的帮助和建议。

最新更新