检查 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呢?

oracle11g dataguard 备库数据同步的检查方法

Oracle 11g Health Monitor Checks