我必须从Oracle外部表中执行许多选择。
我有10个看起来很像这样的游标(ext_temp是外部表)
CURSOR F_CURSOR (day IN varchar,code Number,orig Number)
IS
select NVL(sum(table_4.f),0)
from ext_temp table_4
where
--couple of conditions here, irrelevant for the question at hand.
AND TO_CHAR(table_4.day,'YYYYMMDD') = day
AND table_4.CODE = code
AND table_4.ORIG = orig;
外部表有大约22659个寄存器。
我的脚本主循环看起来像这样
for each register in some_query: --22659 registers
open F_cursor(register.day,register.code,register.orig);
--open 9 more cursors
fetch F_cursor into some_var;
--fetch 9 more cursors, with the same structure
查询已经取得了很大进展。我从here知道我不能有任何索引或DML。
那么,有没有办法让它跑得更快?我可以重写我的plsql脚本,但我不认为我还有时间。
我不是数据库的所有者或DBA。那家伙不希望在他的数据库中有任何额外的信息(大约3gb的数据),外部的表格是我们可以从他身上得到的。他不允许我们创建临时表。我不假装质疑他的理由,但外部表不是解决方案。所以,我们坚持使用它们。
答案 0 :(得分:4)
制作Oracle
个表格。
外部表格可以替换SQL*LOADER
,而不是每天都使用它们。
只要基础文件发生更改,就会运行导入脚本,这会将外部表的内容加载到Oracle
表中。
这是你的同名所想的(从here偷来的):
您正在使用外部表而不是
sqlldr
。使用外部表格
- 在一个语句中将平面文件与现有表合并。
- 在您想要压缩的表格的路上对平面文件进行排序。
- 执行并行直接路径加载 - 不分割输入文件,写入 无数脚本等
- 从存储过程或触发器生效
sqlldr
(插入不是sqlldr
)- 执行多表插入
- 通过流水线plsql函数流式传输数据以进行清理/转换
等等。他们是而不是
sqlldr
- 无需首先使用sqlldr
即可将数据导入数据库。您通常不会在操作系统中每天查询它们,而是使用它们来加载数据。
<强>更新强>
使用3GB
表格无法获得良好的性能,因为Oracle
必须对每个查询执行3GB
全扫描,并且它将是一流的磁盘 - 读取主轴移动的全扫描,而不是一个廉价的缓存模仿,你可以在计划中看到,但在实际执行时间内几乎不会注意到。
尝试说服该人为您创建一个临时表,您可以使用该表来处理数据,只需在会话开始时从外部表加载数据。
这不是最好的解决方案,因为它需要为临时表空间中的每个会话保留表的单独副本,但它在性能方面要好得多。
答案 1 :(得分:3)
如果你必须解决那些没有意义但你无法改变的限制,这真的很难......
您应该最好通过外部表读取一次,然后在代码中的类索引数据结构中构建所需的数据(基本上是一个数组,为您要查找的每个寄存器都有一个元素)。
所以你的光标看起来像这样:
CURSOR F_CURSOR (day IN varchar, orig IN Number)
IS
select NVL(sum(table_4.f),0) value, table_4.CODE register
from ext_temp table_4
where
--couple of conditions here, irrelevant for the question at hand.
AND TO_CHAR(table_4.day,'YYYYMMDD') = day
-- AND table_4.CODE = code -- don't use this condition!
AND table_4.ORIG = orig;
你的寄存器循环将变成一个游标循环:
open F_cursor(register.day,register.orig);
LOOP
fetch F_cursor into some_var;
EXIT WHEN F_cursor%NOT_FOUND
result (some_var.register) := some_var.value;
END LOOP;
结果,不是每个寄存器通过外部表循环,而是只需要一个循环用于所有寄存器。
这可以扩展到你提到的十个游标。
答案 2 :(得分:0)
您可以将外部表数据写入temporary索引(如果需要)表,然后针对它执行多个查询。
create your_temp_table as select * from ext_temp;
create index your_desired_index on your_temp_table(indexed_field);
然后使用your_temp_table直接执行所有查询。
答案 3 :(得分:0)
虽然完全同意Quassnoi的建议,即外部桌子似乎不是这里的正确解决方案,以及DCookie的类比,你被绑在船上并被要求游泳,至少可能有一种结构方式您的程序,以便外部表只读一次。我从你的描述中得到的信念是,所有10个游标都在从外部表中读取,这意味着你强迫Oracle扫描外部表10次。
假设这种推断是正确的,最简单的答案可能是使外部表成为驱动光标,类似于IronGoofy的建议。取决于以下代码段中的some_query
,
for each register in some_query
并且假设查询返回外部表中相同行数的事实并非巧合,最简单的选择是执行类似
的操作FOR register in (select * from ext_temp)
LOOP
-- Figure out if the row should have been part of cursor 1
IF( <<set of conditions>> )
THEN
<<do something>>
-- Figure out if the row should have been part of cursor 2
ELSIF( ... )
...
END LOOP;
或
FOR register in (select *
from ext_temp a,
(<<some query>>) b
where a.column_name = b.column_name )
LOOP
-- Figure out if the row should have been part of cursor 1
IF( <<set of conditions>> )
THEN
<<do something>>
-- Figure out if the row should have been part of cursor 2
ELSIF( ... )
...
END LOOP;
进一步采取措施并将逻辑移出游标(和IF语句)并进入驱动光标应该更有效率。使用上面更简单的代码片段(当然,您可以将some_query
加入这些示例
FOR register in (select a.*,
NVL(sum( (case when condition1 and condition2
then table_4.f
else 0
end) ),
0) f_cursor_sum
from ext_temp table_4)
LOOP
<<do something>>
END LOOP;
如果,即使在这样做之后,您仍然发现您正在进行一些逐行处理,您甚至可以向前迈出一步并从驱动光标执行BULK COLLECT到本地声明的集合并进行操作那个集合。你几乎肯定不想将3 GB的数据提取到本地集合中(虽然破坏PGA可能会导致DBA得出结论临时表不是一件坏事,这不是我建议的),取一些使用LIMIT子句一次一百行应该使事情更有效率。