【问题标题】:bulk collect dynamic sql批量收集动态sql
【发布时间】:2013-08-07 16:47:32
【问题描述】:

我必须编写一个动态 sql 游标,其中有几种可能会生成选择查询。因此,我选择了动态,我正在使用 DBMS_SQL 包来动态创建游标并动态获取数据。

但是,结果集将是巨大的。大约 11GB(有 240 万条记录,并且 select 语句将大约 80 列长,假设每列大约 50Byte varchar)

因此我无法立即打开光标。我想知道是否有一个功能,我可以从 curosr 中获取数据,同时保持 curosr 为 1000 条记录的块打开(我必须动态执行此操作)

请找到附加的代码,它只获取并打印列的值(一个示例案例)我想在这里使用 bul collect \

谢谢

---------------code sample--------------------------------------
--create or replace type TY_DIMDEAL AS TABLE OF VARCHAR2(50) ;
create or replace procedure         TEST_PROC (po_recordset out sys_refcursor)
as


  v_col_cnt   INTEGER;
  v_ind       NUMBER;
  rec_tab     DBMS_SQL.desc_tab;
  v_cursor    NUMBER;
  lvar_output number:=0;
  lvar_output1 varchar2(100);
  lvar_output3 varchar2(100);
  lvar_output2 varchar2(100);
  LVAR_TY_DIMDEAL TY_DIMDEAL;
 lvarcol varchar2(100);
begin
  --
  LVAR_TY_DIMDEAL := TY_DIMDEAL();
  lvar_output1 := '';

  v_cursor := dbms_sql.open_cursor;
  dbms_sql.parse(v_cursor, 'select to_char(Field1) , to_char(fiel2) , to_char(field3) from table,table2 ', dbms_sql.native);
  dbms_sql.describe_columns(v_cursor, v_col_cnt, rec_tab);
  FOR v_pos in 1..rec_tab.LAST LOOP

  LVAR_TY_DIMDEAL.EXTEND();
  DBMS_SQL.define_column( v_cursor, v_pos ,LVAR_TY_DIMDEAL(v_pos),20);
  END LOOP;
 -- DBMS_SQL.define_column( v_cursor, 1 ,lvar_output1,20);
  --DBMS_SQL.define_column( v_cursor, 2 ,lvar_output2,20);
 --DBMS_SQL.define_column( v_cursor, 3 ,lvar_output3,20);
  v_ind := dbms_sql.execute( v_cursor );

  LOOP
    v_ind := DBMS_SQL.FETCH_ROWS( v_cursor );
    EXIT WHEN v_ind = 0;
    lvar_output := lvar_output+1;
   dbms_output.put_line ('row number '||lvar_output)  ;

    FOR v_col_seq IN 1 .. rec_tab.COUNT LOOP  
    LVAR_TY_DIMDEAL(v_col_seq):= '';
     DBMS_SQL.COLUMN_VALUE( v_cursor, v_col_seq,LVAR_TY_DIMDEAL(v_col_seq));
    dbms_output.put_line (LVAR_TY_DIMDEAL(v_col_seq));

   END LOOP;



  END LOOP;

end TEST_PROC;

【问题讨论】:

  • 标题已经提到bulk collect;您是否只是缺少limit clause 以使批次更小?有一篇关于这个here的文章。
  • @AlexPoole 如何将bulk collect 应用于使用DBMS_SQL 和动态列数构造的游标?
  • @ThinkJet - 我相信您可以使用bulk column definitions,但我没有示例可手。不过,使用execute immediate 可能更容易,如果需要,可以转换引用光标以获取元数据。我猜limit 会暗示这一点,而我并不是真的打算这样做......
  • @AlexPoole 感谢您指出这种可能性。可以在Oracle documentation for DBMS_SQL package 找到示例。

标签: oracle collections plsql oracle11g


【解决方案1】:

以合理大小的块从游标中获取数据,同时保持游标打开,是PL/SQL Best Practices 之一。

上述文档(请参阅Code 38 item)概述了一种方法,用于在运行时才知道选择列表。基本上:

  1. 定义一个合适的类型来获取结果。假设所有返回的列都是VARCHAR2的类型:

    -- inside DECLARE
    Ty_FetchResults IS TABLE OF DBMS_SQL.VARCHAR2_TABLE;
    lvar_results Ty_FetchResults;
    
  2. 在每次调用DBMS_SQL.FETCH_ROWS 之前,调用DBMS_SQL.DEFINE_ARRAY 以启用批量提取。

  3. 调用DBMS_SQL.FETCH_ROWS从游标中获取1000行。
  4. 调用DBMS_SQL.COLUMN_VALUE 将获取的数据复制到您的结果数组中。
  5. FOR 循环中逐条记录处理结果。不要担心获取的记录数:如果有记录要处理,FOR 循环将正确运行;如果结果数组为空,FOR 循环将不会运行。
  6. 当提取的记录数小于预期大小时退出循环。
  7. 记得DBMS_SQL.CLOSE光标。

你的循环体应该是这样的:

LOOP
  FOR j IN 1..v_col_cnt LOOP
    DBMS_SQL.DEFINE_ARRAY(v_cursor, j, lvar_results(j), 1000, 1);
  END LOOP;

  v_ind := DBMS_SQL.FETCH_ROWS(v_cursor);

  FOR j IN 1..v_col_cnt LOOP
    lvar_results(j).DELETE;
    DBMS_SQL.COLUMN_VALUE(v_cursor, j, lvar_results(j));
  END LOOP;

  -- process the results, record by record
  FOR i IN 1..lvar_results(1).COUNT LOOP
    -- process a single record...
    -- your logic goes here
  END LOOP;

  EXIT WHEN lvar_results(1).COUNT < 1000;
END LOOP;

-- don't forget: DBMS_CLOSE(v_cursor);

另见Doing SQL from PL/SQL: Best and Worst Practices

【讨论】:

    【解决方案2】:

    LIMIT CLAUSE 可以来救援!

    PL/SQL 集合本质上是内存中的数组,如此庞大 集合可能会对系统性能产生不利影响,因为 他们需要的内存量。在某些情况下,它可能是 需要将正在处理的数据拆分成块以使 代码对内存更友好。这种“分块”可以使用 BULK COLLECT 语法的 LIMIT 子句。

    您可以在 BULK COLLECT INTO CLAUSE 之后使用 LIMIT CLAUSE 来限制您的 RS。 超过限制后,您可以获取剩余的行。 看到这篇文章 http://www.dba-oracle.com/plsql/t_plsql_limit_clause.htm

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-28
      • 1970-01-01
      • 2015-04-26
      • 1970-01-01
      相关资源
      最近更新 更多