【问题标题】:optimizing insert query优化插入查询
【发布时间】:2020-08-18 21:31:36
【问题描述】:

我正在尝试在全局表中插入逗号分隔值。当数据很大时,处理数据需要很长时间。我需要优化我的插入查询,有没有其他方法可以实现下面的插入语句以获得更好的优化?请检查下面的代码以获取更多信息。感谢您提供任何帮助。

//my proc
emp_id in CLOB;


//insert statement
insert into Global_Emp_Tbl
with inputs(str) as(
select to_clob(emp_id)
from dual
),
temp_table(s, n, empid, st_pos, end_pos) as (
select ',' || str || ',', -1, null, null, 1
from inputs

union all
selct s, n+1, substr(s, st_pos, end_pos - st_pos),
end_pos + 1, instr(s, ',', 1, n+3)
from temp_table
where end_pos != 0
)
select empid from temp_table where empid is not null;
commit;

//using insert table in where clause
exists( select 1 from Global_Emp_Tbl gt  where e.id =gt.emp_id ) //joining with main table

【问题讨论】:

  • 通常避免数据库中的 CSV 数据 - 这使得它更难使用。
  • @AndrewMorton 我该如何避免呢?我必须在 proc 中发送数据来处理/过滤数据
  • @AndrewMorton 我在这里非常有限,我唯一能做的就是创建全局临时表。
  • Global_Emp_Tbl.emp_id 的数据类型是什么?是VARCHAR2 还是CLOB

标签: sql oracle stored-procedures


【解决方案1】:

我无法在Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production 中重现您的问题

此过程提供样本数据,前 N 个整数的 CLOB csv 列表

create or replace function get_list(N NUMBER) return CLOB
as
 v_lst CLOB;
 i PLS_INTEGER;
BEGIN
  v_lst :='1';
  for  i  in 2 .. n loop
     v_lst :=v_lst||','||to_char(i);
  end loop;   
  return(v_lst);
END;
/

请注意,5K 参数给出了大约 20K 长的列表。

select length(get_list(5000)) from dual;
23892

在全局临时表中解析此列表在 内完成,而不是在 分钟 内完成。这是一个使用您的SELECT的示例

SQL> set timi on;
SQL> create global temporary table csv_tbl
  2  ON COMMIT PRESERVE ROWS
  3  as
  4  with inputs(str) as(
  5  select get_list(5000)
  6  from dual
  7  ),
  8  temp_table(s, n, empid, st_pos, end_pos) as (
  9  select ',' || str || ',', -1, null, null, 1
 10  from inputs
 11  union all
 12  select s, n+1, substr(s, st_pos, end_pos - st_pos),
 13  end_pos + 1, instr(s, ',', 1, n+3)
 14  from temp_table
 15  where end_pos != 0
 16  )
 17  select  empid  from temp_table where empid is not null
 18  ;

Table created.

Elapsed: 00:00:01.35

所以最可能的解释是,花费最多的时间是用EXISTS 子句进行查询

exists( select 1 from Global_Emp_Tbl gt  where e.id =gt.emp_id )

我想到了两个问题

1) 临时表和EMP 表中EMP_ID 的数据类型不同,例如:在临时表中为VARCHAR2,在EMP 表中为NUMBER - 这将禁止在EMP 表上使用索引

2) 全局临时表上缺少的对象统计信息导致CBO 出现错误的执行计划,而您使用索引在一个大嵌套循环),您应该使用全表扫描

【讨论】:

    【解决方案2】:

    你可以使用简单的 REGEXP_SUBSTR 来实现同样的效果

        insert into Global_Emp_Tbl
        SELECT Regexp_substr(empid, '[^,]+', 1, LEVEL) AS empid
        FROM   (SELECT To_clob(emp_id) empid
                FROM   dual)
        CONNECT BY LEVEL <= Regexp_count(emp_id, '[^,]+');
        commit;
    

    另外,还有一个建议尝试在您的函数中更改以下内容

     select empid from temp_table where n > 0;
    

    【讨论】:

    • 我认为如果 empid 大于 4000 字节,这会使应用程序崩溃。
    • 我测试了这两个函数都在这种情况下崩溃 rpad(1,1999,1)||','||rpad(1,1999,1)||','||rpad(1 ,1999,1)||','||rpad(1,1999,1)
    • 我通过 20k id 测试了我的查询并且它是成功的,不好的部分是我花了超过一分钟来处理 20k id。我将 empid 作为字符串传递。
    • 你试过用 regexp_substr 测试它吗
    • 是的,我做到了,它没有使我的应用程序崩溃,但该过程比我的查询慢得多。感谢您的努力
    猜你喜欢
    • 1970-01-01
    • 2014-03-21
    • 2019-06-03
    • 1970-01-01
    • 2019-01-27
    • 1970-01-01
    • 1970-01-01
    • 2012-10-06
    • 2023-03-03
    相关资源
    最近更新 更多