如何在 APEX 应用程序的 csv 文件的批量数据插入中使用 forall 语句

Posted

技术标签:

【中文标题】如何在 APEX 应用程序的 csv 文件的批量数据插入中使用 forall 语句【英文标题】:How to use forall statement in bulk data insert of csv file in APEX application 【发布时间】:2017-09-11 13:25:17 【问题描述】:

我正在使用下面的代码使用 APEX 5 将数据从 csv 文件上传到 db 表,但性能非常慢,因为 .csv 文件非常大。如何在下面的代码中使用 forall 语句来更快地运行查询?我试图用 forall 替换执行立即语句,但它不起作用。有人可以指导我吗?

    DECLARE
   v_blob_data         BLOB;
   v_blob_len          NUMBER;
   v_position          NUMBER;
   v_raw_chunk         RAW (10000);
   v_char              CHAR (1);
   c_chunk_len         NUMBER := 1;
   v_line              VARCHAR2 (32767) := NULL;
   v_data_array        wwv_flow_global.vc_arr2;
   v_rows              NUMBER;
   v_sr_no             NUMBER := 1;
   v_filename          VARCHAR2 (2000)
                          := APEX_UTIL.get_session_state ('P4_FILE_BROWSE');
   v_idx               NUMBER;
   v_first_line_done   BOOLEAN := FALSE;
   v_error_cd          NUMBER := 0;

   TYPE my_rec IS RECORD
   (
      company_code   XXX_COMP_TABLE.company_code%TYPE,
      cost_center    XXX_COMP_TABLE.cost_center%TYPE,
      user_name      XXX_COMP_TABLE.user_name%TYPE,
      start_date     XXX_COMP_TABLE.effective_start_date%TYPE,
      end_date       XXX_COMP_TABLE.effective_end_Date%TYPE
   );

   TYPE v_coll_rec IS TABLE OF my_rec     INDEX BY PLS_INTEGER;

   v_col1 v_coll_rec;

BEGIN
   SELECT blob_content
     INTO v_blob_data
     FROM apex_application_temp_files
    WHERE name = v_filename;

   v_blob_len := DBMS_LOB.getlength (v_blob_data);
   v_position := 1;

   --wwv_flow.debug('v_position'||v_position);
   LOOP
      v_idx := DBMS_LOB.INSTR (v_blob_data, '0A', v_position);

      -- wwv_flow.debug('v_idx'||v_idx);
      IF v_idx = 0
      THEN
         wwv_flow.debug ('in if');
         v_line :=
            UTL_RAW.cast_to_varchar2 (
               DBMS_LOB.SUBSTR (v_blob_data, 32767, v_position));
      ELSE
         -- wwv_flow.debug('in else');
         v_line :=
            UTL_RAW.cast_to_varchar2 (
               DBMS_LOB.SUBSTR (v_blob_data, v_idx - v_position, v_position));
      END IF;

      wwv_flow.debug ('v_line' || v_line);
      EXIT WHEN v_idx = 0;
      v_position := v_idx + 1;
      v_line := REPLACE (v_line, ',', ':');
      v_data_array := wwv_flow_utilities.string_to_table (v_line);

      IF (v_first_line_done != TRUE)
      THEN
         v_first_line_done := TRUE;

         --wwv_flow.debug('v_data_array(5) '||v_data_array(5) );
         IF     v_data_array (1) = 'COMPANY_CODE'
            AND v_data_array (2) = 'COST_CENTER_CODE'
            AND v_data_array (3) = 'USER_NAME'
            AND v_data_array (4) = 'EFFECTIVE_START_DATE' --AND v_data_array(5) = 'EFFECTIVE_END_DATE'
         THEN
            v_error_cd := 0;
         ELSE
            v_error_cd := 1;
         END IF;
      ELSIF (v_first_line_done = TRUE AND v_error_cd = 0)
      THEN
         FORALL indx IN 1 .. v_col1.COUNT
            INSERT
              into XXX_COMP_TABLE
            VALUES v_col1 (indx);
      END IF;

      v_line := NULL;
      v_sr_no := v_sr_no + 1;
   END LOOP;
END;

【问题讨论】:

APEX 5 与此问题无关 - 已删除标签。 向我们展示您的forall 代码。没有理由将execute immediate 用于静态 SQL。 我正在尝试使用此代码通过 APEX 将大型 csv 文件上传到 db 表中。由于性能问题,我被要求使用 forall 语句调整此查询,但我不确定如何使用 forall 语句在这段代码中。我尝试过但无法在 forall 代码上进行开发,因为我仍然对如何插入 forall 语句感到困惑。你能指导我吗? @Tony Andrews 我已经用 forall 语句修改了下面的代码,但由于其显示的 Count 必须声明为错误而无法正常工作- 我无法发布代码,因为它的 hwoing 太长 bt 2000 字符。我可以把代码发给你吗? 【参考方案1】:
DECLARE
  v_blob_data BLOB;
  v_blob_len NUMBER;
  v_position NUMBER;
  v_raw_chunk RAW(10000);
  v_char      CHAR(1);
  c_chunk_len NUMBER          := 1;
  v_line      VARCHAR2 (32767):= NULL;
  v_data_array wwv_flow_global.vc_arr2;
  v_rows            NUMBER;
  v_sr_no           NUMBER         := 1;
  v_filename        VARCHAR2(2000) := apex_util.get_session_state('P4_FILE_BROWSE' );
  v_idx             NUMBER;
  v_first_line_done BOOLEAN := false;
  v_error_cd        NUMBER  :=0;

  TYPE my_rec IS RECORD ( company_code XXX_COMP_TABLE.company_code%type , cost_center XXX_COMP_TABLE.cost_center%type  , 
  user_name XXX_COMP_TABLE.user_name%type , start_date  XXX_COMP_TABLE.effective_start_date%type, end_date XXX_COMP_TABLE.effective_end_Date%type); 
  TYPE v_coll_rec IS TABLE OF my_rec INDEX BY PLS_INTEGER; 
BEGIN

  SELECT blob_content
  INTO v_blob_data
  FROM apex_application_temp_files
  WHERE name  = v_filename;
  v_blob_len := dbms_lob.getlength(v_blob_data);
  v_position := 1;
  --wwv_flow.debug('v_position'||v_position);
  LOOP
    v_idx := dbms_lob.instr( v_blob_data, '0A', v_position );
   -- wwv_flow.debug('v_idx'||v_idx);
    IF v_idx = 0 THEN
      wwv_flow.debug('in if');
      v_line := utl_raw.cast_to_varchar2( dbms_lob.substr( v_blob_data, 32767, v_position ) );
    ELSE
     -- wwv_flow.debug('in else');
      v_line := utl_raw.cast_to_varchar2( dbms_lob.substr( v_blob_data, v_idx - v_position, v_position ) );
    END IF;
    wwv_flow.debug('v_line'||v_line);
    EXIT
  WHEN v_idx              = 0;
    v_position           := v_idx + 1;
    v_line               := REPLACE (v_line, ',', ':');
    v_data_array         := wwv_flow_utilities.string_to_table (v_line);
    IF(v_first_line_done != true) THEN
       v_first_line_done := true;

       --wwv_flow.debug('v_data_array(5) '||v_data_array(5) );
          IF v_data_array(1)  = 'COMPANY_CODE' AND v_data_array(2) = 'COST_CENTER_CODE' AND v_data_array(3) = 'USER_NAME' 
                                AND v_data_array(4) = 'EFFECTIVE_START_DATE' --AND v_data_array(5) = 'EFFECTIVE_END_DATE'
                                THEN
             v_error_cd     := 0;
          ELSE
            v_error_cd := 1;
          END IF;                   
    ELSIF(v_first_line_done = true AND v_error_cd = 0) THEN    
    FORALL indx IN 1 .. v_coll_rec.COUNT 
INSERT INTO 
( SELECT company_code, 
cost_center, 
user_name , 
start_date, 
end_date 
FROM XXX_COMP_TABLE
) 
VALUES v_coll_rec (indx) ;          
    END IF; 
  v_line := NULL; v_sr_no := v_sr_no + 1;  
END LOOP;
END;

【讨论】:

你忘了删除v_col1 v_coll_rec;创建的那个类型的变量,直接使用了错误的类型。将此添加到声明中,并将代码中所有位置的v_coll_rec 替换为v_col1。也插入是错误的 @XING 感谢您验证代码。插入语句显示错误 - PL/SQL: ORA-00947: not enough values 检查我上面的回答。我已根据您发布的代码更正它 @XING 对不起,我看不到你的答案。代码仍然没有显示足够的值。

以上是关于如何在 APEX 应用程序的 csv 文件的批量数据插入中使用 forall 语句的主要内容,如果未能解决你的问题,请参考以下文章

Oracle APEX - 将隐藏的 SQL 查询下载到 CSV

如何在 Javascript 或 Apex 中检查受限文件

如何批量将csv文件转换成xls文件

如何批量读取csv格式的文件名及文件内容到新的Excel中?

如何使用 Apache Apex 将数据从 DB2 批量摄取到 Vertica

Django Admin——批量员工用户创建/从 CSV 文件导入