【问题标题】:Nested cursor performance tuning嵌套游标性能调优
【发布时间】:2020-11-10 22:10:58
【问题描述】:

我有 2 个游标,一个用于从 50 列和 10,000 + 数据的表中获取记录,另一个用于检查另一个大表(200 万数据)中是否存在特定列。我应该将光标 1 中的所有记录写入文件一年,如果该列存在于光标 2 中,那么我应该打印一条存在的错误消息,而不是删除它们。如果它不存在,那么我应该删除该行并将其写入与删除记录相同的文件和消息中。 我使用了嵌套游标,性能太差了,因为它每次都在处理从游标 1 到游标 2 的每一行。

       CURSOR cursor1
         IS           
             select a.* ,a.rowid
              FROM table1 a 
              WHERE a.table1.year = p_year;
    
    CURSOR check_c2(lv_cd )
     IS
      Select DISTINCT 'Y' 
       from table2
       where table2 ='R'
       AND table2.year= p_year
       and table2_code= lv_cd ;

BEGIN :
   FOR r in cursor1 LOOP
            EXIT WHEN cursor1%NOTFOUND;
        
              OPEN check_c2(r.cd);
              FETCH check_c2 INTO lv_check;
                IF check_c2%NOTFOUND THEN
                    lv_check :='N';
                END IF;
                CLOSE check_c2;
       
                  IF lv_check ='Y' THEN
                   lv_msg =(r.col1,r.col2....r.col50, R code exists do not delete)
                   utl_file.put_line(lv_log_file, lv_msg, autoflush=>TRUE);
                   
                ELSE 
                  DELETE from table1 where rowid= r.rowid
                  lv_msg =(r.col1,r.col2....r.col50, delete row)
                   utl_file.put_line(lv_log_file, lv_msg, autoflush=>TRUE);
                   END IF;
                   END LOOP;

【问题讨论】:

    标签: sql oracle performance plsql cursor


    【解决方案1】:

    没有足够的声誉来写 cmets som 会写作为答案。

    您没有尝试添加一些时间标记来了解哪些部分花费的时间最多吗?

    table2 有按年份和代码的索引吗? cursor2 查询的解释计划是什么?如果是 - 年份+代码组合平均有多少行? 如果从表 2 中选择的总体数据量很大 - 那么在表 2 上按年进行全扫描/索引范围扫描、分组和散列左外连接从表 1 到表 2 的单个查询可能会更快

    select a.*, a.rowid, nvl2(c.code, 'Y', 'N') check_col
    from table1 a,
    (
        select distinct code
        from table2 b
        where b.year = p_year
    ) c
    where a.year = p_year
      and c.code(+) = a.cd
    

    【讨论】:

    • @Doranaut 我试过单查询,性能比以前好多了。谢谢
    • @Doranaut - 当我尝试向 table1 a 添加更多条件时,它会一直运行。我只是在join之前使用and来添加条件
    • @arsha 其他条件可能会更改执行计划。查询文本和它的执行计划至少要说什么。
    【解决方案2】:

    这个怎么样?三步操作:

    第 1 步:“保存”您稍后将删除的行

    create table log_table as
    select *
    from table1 a
    where exists (select null
                  from table2 b
                  where b.year = a.year
                    and b.code = a.code
                 );
    

    第 2 步:删除行:

    delete from table1 a
    where exists (select null
                  from table2 b
                  where b.year = a.year
                    and b.code = a.code
                 );
    

    第 3 步:如果必须,将 LOG_TABLE 中保存的行存储到您的该文件中。如果没有,请将它们留在LOG_TABLE

    【讨论】:

    • 就像将它存储在临时表中一样。恐怕我做不到。
    【解决方案3】:
    循环中的

    utl_file.put_line 将是一个开销。尝试附加到 lv_msg 直到字符串的长度为 32767 字节并且只写入一次。 这肯定会减少 I/O 并提高性能。

    【讨论】:

    • 我应该追加 utl_file.put_line 而不是写入它吗?
    • 你不会想在中间剪掉 lv_msg 的记录。继续使用新行(chr(10))附加到lv_msg,当长度小于32767时写入文件并将lv_msg重置为null。
    • 我试过了,性能只提高了几分钟。谢谢
    猜你喜欢
    • 1970-01-01
    • 2012-01-18
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多