检查 Oracle DB .shellscript 作业中的大量记录超时
Posted
技术标签:
【中文标题】检查 Oracle DB .shellscript 作业中的大量记录超时【英文标题】:Checking Huge Volume of records in Oracle DB .shellscript job times out 【发布时间】:2018-10-21 10:03:50 【问题描述】:我正在从事一项日常自动化工作,该工作会根据 oracle 数据库检查文本文件中的记录。 我们每天都会收到来自外部团队的文本文件,其中包含大约 100,000 条记录。文本文件将采用 unix 格式,其中有 6 列,由 | 分隔。符号。
例如, HDR 1
home/sample/file|testvalue1|testvalue2|testval3|testval4|testval5
TRL
我需要检查 testval3 和 testval5 中的值是否存在于我的 oracle 数据库中的表中。该表有大约 1000 万条记录。 我目前正在通过 shellscript 处理它。在 shellscript 中,我正在读取文本文件并循环遍历每一行。在循环内部,我从每一行传递值并对 DB 运行查询。如果数据库中不存在记录,我必须将它们输出到 csv 文件。使用以下查询:
select ‘testval3’,’testval5’ from dual
where not exists (select primarykeycolumn
from mytable where mycolumn=testval3 and mycolumn2=testval5)
由于输入文件有 100000 个条目,我的循环将运行查询 100000 次,每次它都会检查包含 1000 万条记录的表。这使我的批处理作业运行了好几个小时,我必须终止它。有没有更好的方法来处理这种情况?如果没有更好的方法通过 shellscript 执行此操作,我也可以使用 java。
【问题讨论】:
您使用的是 UTL_FILE?,您使用的是哪个版本的 Oracle? 您似乎选择了效率最低的方法。你没听说过 SQL * Loader、外部表等吗?我的建议是首先在数据库中创建一个包含 2 列的表,然后使用 SQL* Loader 将文件加载到其中。然后使用单个查询,您可以提取您需要的所有内容。与加载程序不同,外部表和 UTL_FILE 需要目录权限。如果你能轻松获得它,那就去吧。 @KaushikNayak 他非常清楚自己的方式效率低下,这就是他首先在这里提出问题的原因。你可以给出相同的值,只是省略前两个短语。 @Busybee:我已经发布了一个答案,可能是您正在寻找的东西。 @VinkoVrsalovic:你怎么知道是“他”? .此外,我不知道这样的问题在 java 标记下是否很少见,但是在 Oracle 下,像 SQL* Loader 这样的东西是人们期望 OP 知道的基本内容。我们需要连接数据库一百万次的逻辑并没有给我留下深刻的印象。那么问题来了,为什么要使用它。 【参考方案1】:执行此操作的快速方法是从脚本开头的表中收集所有可用的 testval3 和 testval5 组合,将它们存储在哈希表或类似结构中,以便在阅读每一行时轻松查询内存中的本地数据结构。
它肯定会使用更多内存,但它会运行单个验证查询并多次加速程序。
要运行的查询将是 select distinct mycolumn,mycolumn2 from mytable
或等效的。
见
How do I (or can I) SELECT DISTINCT on multiple columns? 和
ORACLE Select Distinct return many columns and where
所以,总而言之,我提出的机制是:
运行查询以从表中选择所有不同的 testval3 和 testval5 对
创建一个哈希表和一个存储在上面的特定 Pair 结构。例如,在 Java 中,您可以使用 A Java collection of value pairs? (tuples?),然后,如果您的列的类型是字符串,请使用类似
的内容HashMap<Pair<String,String>, boolean> pairMap
确保实现 hashCode 和 equals 方法,就像它们在其他示例答案中一样,以便您可以正确地将其用作地图上的键(如果使用 Java 或类似方法)
-
将查询结果存储在哈希表中,将 testval3 和 testval5 对作为表上的键,将 true 作为值(如何迭代结果集留给读者练习):
pairMap.put(new Pair<String,String>(testval3,testval5),true)
-
逐行读取文件
对于每行,查找哈希表中是否存在一对 testval3 和 testval5,如果不存在则将该行输出到 CSV。为此,您只需查询地图并检查是否为空 (Key existence check in HashMap)
例如:
if (pairMap.get(new Pair<String,String>(testval3,testval5)) == null)
//output to CSV
最后,正如@Kaushik Nayak 所说,@Vivek 在 cmets 中暗示的另一个选项是使用其数据加载工具将文件加载到 Oracle,然后对不存在的值运行单个查询。
【讨论】:
感谢您的建议。如果第一种方法不起作用,将尝试此方法。【参考方案2】:以下是一种简单的解决方案,可确保不存在超时,甚至您无需扫描数百万条记录 10 万次。
一次性设置: 创建临时临时表:
create table a_staging_table(
testvalue1 varchar2(255),
testvalue2 varchar2(255),
testval3 varchar2(255),
testval4 varchar2(255),
testval5 varchar2(255)
);
----循环过程
将您的“CSV/TEXT”数据加载到临时表中:
some_file_name.ctl:此文件包含以下加载数据命令。
load data
INFILE 'home/sample/file.csv'
INTO TABLE a_staging_table
APPEND FIELDS TERMINATED BY '|'
(testvalue1,testvalue2,testval3,testval4,testval5);
现在,运行 SQL 加载器将数据加载到临时表表单中。
sqlldr userid=dbUserName/dbUserPassword control=some_file_name.ctl log=some_file_name.log
您的数据已加载到临时表中。现在加入 staging 表和 your_original_table 以识别不存在的记录。
第一种方式: 使用 SQL*PLUS 后台处理以下 SQL 的输出:
select s.testval3,testval5
from (select distinct testval3,testval5
from a_staging_table) s
where not exists
(select 1
from your_original_table
where mycolumn1=s.testval3
and mycolumn2=s.testval5);
第二种方式:
Begin
for x in (
select s.testval3,testval5
from (select distinct testval3,testval5
from a_staging_table) s
where not exists
(select 1
from your_original_table
where mycolumn1=s.testval3
and mycolumn2=s.testval5)
) loop
DBMS_OUTPUT.put_line('testval3: '||x.testval3 || ' ------ '||'testval5: '||x.testval5);
--write all these values into another file saying that these are not matching values, using UTL_FILE.
--Then finally truncate the table "a_staging_table"
--so that this data will not available next time, and next time again process will run with different file
end loop;
【讨论】:
无需再次建议循环。 OP 可以将输出假脱机到 csv。此外,group by
可以替换为 distinct
@KaushikNayak:感谢您的指出,我已将 GROUP BY 替换为 DISTINCT
不客气。但是,我主要关心的是loop
,这不是必需的。如果 OP 可以使用 sqlloader,那么他们也可以使用 shell 脚本中的 sqlplus 命令行来简单地假脱机输出。
@KKK,感谢您的详细解释。对不起,我没有先提到这一点,testval5 值将是一些用逗号分隔的值。例如 home/sample/file|testvalue1|testvalue2|testval3|testval4|test1,test2
例如 home/sample/file|testvalue1|testvalue2|testval3|testval4|test1,test2,test3。 test1、test 2、test 3 的值是 varchar2,我需要对照查询中提到的“mycolumn2”检查它们。 original_table 中的 mycolumn2 始终只有一个值,例如 test1 或 test2 。有没有办法将值传递到暂存表,例如 testvalue1,testvalue2,tesval3,testval4,test1 作为 1 行,testvalue1,testvalue2,tesval3,testval4,test2 作为另一个等等?以上是关于检查 Oracle DB .shellscript 作业中的大量记录超时的主要内容,如果未能解决你的问题,请参考以下文章
Oracle db view/system table 检查包的给定部分是过程还是函数
[dataguard同步数据库]oracle11g dataguard 备库数据同步的检查方法
在完成MySQL导入后在链接的容器上执行shellscript
在oracle中创建db link 已经确定创建成功了。在创建视图时为啥还会报不能找到db link呢?