批量收集动态sql

时间:2013-08-06 15:21:02

标签: oracle collections plsql oracle11g

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

然而,结果集将是巨大的。大约11GB(有240万条记录,选择语句大约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;

2 个答案:

答案 0 :(得分:3)

在保持光标打开的同时从光标中获取合理大小的块中的数据是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光标。

  8. 你的循环体看起来像这样:

    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

答案 1 :(得分:0)

限制条款可以救援!

  

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

您可以在批量收集条款后限制您的RS使用限制条款。 在您超出限制之后,您可以获得剩余的行数。 看这篇文章 http://www.dba-oracle.com/plsql/t_plsql_limit_clause.htm