Export data into Excel using OLE OBJECTS vs. GUI_DOWNLOAD

本文介绍使用ABAP通过GUI_DOWNLOAD及OLEObjects两种方式从内部表导出数据到Excel的方法。GUI_DOWNLOAD将所有字段数据置于单个单元格中,操作简便但格式受限;OLEObjects则允许指定数据放置的单元格及样式,灵活性高但处理速度较慢。

This post displays a program code that exports data from internal table into an excel document using two different methods namely, OLE Objects and function module GUI_DOWNLOAD.

Initial screen of this is below.

1

With GUI_DOWNLOAD, all field data of the internal table will be in ONLY ONE cell of the generated excel file. This option is faster than OLE since all data fields were placed into one excel cell.

2

But with OLE Objects, you can specify in what excel cell the specific data field of the internal will be placed after the export process. You can also specify font style, font colors, font weight, cell borders, and etc.

The disadvantage of this option is this will take time to export data especially dealing with large number of data rows. This is because of the processing of putting specific data field value into a specific cell number. The styles mentioned also will affect the performance of export process.

3

Create a sample program and paste the following codes below.

REPORT  ZPROGRAMTEST_BERT.
*&———————————————————————*
*&  INCLUDES
*&———————————————————————*
INCLUDE ole2incl. “for OLE export data to excel
*&———————————————————————*
*&  DATA DECLARATIONS
*&———————————————————————*
DATA: “internal table and workarea
      i_mara      TYPE STANDARD TABLE OF mara,
      x_mara      TYPE mara,
      “variables
      v_file      TYPE string,
      v_filename  TYPE string,
      v_path      TYPE string,
      v_fullpath  TYPE string,
      v_row       TYPE i,
      “excel objects
      o_excel     TYPE ole2_object,        “ excel object
      o_mapl      TYPE ole2_object,        “ list of workbooks
      o_map       TYPE ole2_object,        “ workbook
      o_zl        TYPE ole2_object.        “ cell
*&———————————————————————*
*&  START-OF-SELECTION.
*&———————————————————————*
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME.
  PARAMETERS: p_file TYPE rlgrap-filename.
  ULINE.
  PARAMETERS: rb_guidl RADIOBUTTON GROUP g1,
              rb_oledl RADIOBUTTON GROUP g1.
SELECTION-SCREEN END OF BLOCK b1.
*&———————————————————————*
*&  AT SELECTION-SCREEN
*&———————————————————————*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
  “call the dialog for directory
  CALL METHOD cl_gui_frontend_services=>file_save_dialog
    EXPORTING
      default_extension = ’xls’
      default_file_name = ’Exported_File’
      initial_directory = ’c:\temp\’
    CHANGING
      filename          = v_filename
      path              = v_path
      fullpath          = v_fullpath.
  MOVE v_fullpath TO p_file.
*&———————————————————————*
*&  START-OF-SELECTION.
*&———————————————————————*
START-OF-SELECTION.
  PERFORM f_get_data.
*&———————————————————————*
*&  END-OF-SELECTION.
*&———————————————————————*
END-OF-SELECTION.
  PERFORM f_export_data.
*&———————————————————————*
*&  FORM f_get_data.
*&———————————————————————*
*&  Retrieve data from table
*&———————————————————————*
FORM f_get_data.
  REFRESH: i_mara.
  SELECT *
    FROM mara
    INTO TABLE i_mara
    UP TO 20 ROWS.
  IF sy-subrc EQ 0.
  ENDIF.
ENDFORM.                    “ f_get_data
*&———————————————————————*
*&  FORM f_export_data.
*&———————————————————————*
*&  Export data depending on what was selected in the screen
*&———————————————————————*
FORM f_export_data.
  IF rb_guidl IS NOT INITIAL.
    PERFORM f_export_gui_download.
  ELSEIF rb_oledl IS NOT INITIAL.
    PERFORM f_export_ole_download.
  ENDIF.
ENDFORM.                    “ f_export_data
*&———————————————————————*
*&  FORM f_export_gui_download.
*&———————————————————————*
*&  Export data using function module GUI_DOWNLOAD
*&———————————————————————*
FORM f_export_gui_download.
  CALL FUNCTION ’GUI_DOWNLOAD’
    EXPORTING
      filename                        = v_fullpath
    TABLES
      data_tab                        = i_mara
    EXCEPTIONS
      FILE_WRITE_ERROR                = 1
      NO_BATCH                        = 2
      GUI_REFUSE_FILETRANSFER         = 3
      INVALID_TYPE                    = 4
      NO_AUTHORITY                    = 5
      UNKNOWN_ERROR                   = 6
      HEADER_NOT_ALLOWED              = 7
      SEPARATOR_NOT_ALLOWED           = 8
      FILESIZE_NOT_ALLOWED            = 9
      HEADER_TOO_LONG                 = 10
      DP_ERROR_CREATE                 = 11
      DP_ERROR_SEND                   = 12
      DP_ERROR_WRITE                  = 13
      UNKNOWN_DP_ERROR                = 14
      ACCESS_DENIED                   = 15
      DP_OUT_OF_MEMORY                = 16
      DISK_FULL                       = 17
      DP_TIMEOUT                      = 18
      FILE_NOT_FOUND                  = 19
      DATAPROVIDER_EXCEPTION          = 20
      CONTROL_FLUSH_ERROR             = 21
      OTHERS                          = 22.
  IF sy-subrc <> 0.
* Implement suitable error handling here
  ELSE.
    WRITE: ’Export data to excel using GUI_DOWNLOAD successful.’.
  ENDIF.
ENDFORM.                    “ f_export_gui_download
*&———————————————————————*
*&  FORM f_export_ole_download.
*&———————————————————————*
*&  Export data using OLE EXCEL
*&———————————————————————*
FORM f_export_ole_download.
  “create excel document
  PERFORM f_notification USING ’Creating excel document…’.
  CREATE OBJECT o_excel ’EXCEL.APPLICATION’.
  PERFORM f_check_ole_err.

  “set visibility to background
  SET PROPERTY OF o_excel ’Visible’ = 0.

  “get list of workbooks, initially empty
  CALL METHOD OF o_excel ’Workbooks’ = o_mapl.
  PERFORM f_check_ole_err.

  “add a new workbook
  call method of o_mapl ’Add’ = o_map.
  PERFORM f_check_ole_err.

  “fill data to header of the excel file
  CLEAR: v_row.
  PERFORM f_fill_header.

  “download data to excel
  PERFORM f_notification USING ’Downloading data to excel…’.
  PERFORM f_download_full.

  “save the excel file
  CALL METHOD OF o_map ’SAVEAS’ EXPORTING #1 = p_file.
  PERFORM f_check_ole_err.
  CALL METHOD OF o_map ’CLOSE’.
  PERFORM f_check_ole_err.

  “quit excel application
  CALL METHOD OF o_excel ’QUIT’.
  PERFORM f_check_ole_err.

  “successful message
  WRITE: ’Export data to excel using OLE successful.’.

  “free the objects
  FREE OBJECT: o_excel,
               o_mapl,
               o_map,
               o_zl.
ENDFORM.                    “ f_export_ole_download
*———————————————————————-*
* FORM f_notification.
* –> inform user on what is going on using SAPGUI_PROGRESS_INDICATOR
*———————————————————————-*
FORM f_notification USING pi_message TYPE string.
  CALL FUNCTION ’SAPGUI_PROGRESS_INDICATOR’
    EXPORTING
     text = pi_message.
ENDFORM.                   “ f_notification
*———————————————————————-*
* FORM f_check_ole_err.
* –> check if OLE processing if there is an error occured
*———————————————————————-*
form f_check_ole_err.
  if sy-subrc NE 0.
    MESSAGE ’OLE-Automation Error.’ TYPE ’E' DISPLAY LIKE ’S’.
    STOP.
  endif.
endform.                    “ f_check_ole_err
*———————————————————————-*
* FORM f_fill_header.
* –> set value to header for the excel file
*———————————————————————-*
FORM f_fill_header.
  v_row = v_row + 1.
  PERFORM f_fill_cell USING v_row 1 ’Material’.
  PERFORM f_fill_cell USING v_row 2 ’Created On’.
  PERFORM f_fill_cell USING v_row 3 ’Created by’.
  PERFORM f_fill_cell USING v_row 4 ’Last Change’.
  PERFORM f_fill_cell USING v_row 5 ’Changed by’.
ENDFORM.                    “ f_fill_header
*———————————————————————-*
* FORM f_fill_cell.
* –> fill data to excell cell
*———————————————————————-*
form f_fill_cell USING pi_row TYPE i
                       pi_col TYPE i
                       pi_val TYPE ANY.
  CALL METHOD OF o_excel ’Cells’ = o_zl
    EXPORTING
      #1 = pi_row
      #2 = pi_col.
  SET PROPERTY OF o_zl ’Value’ = pi_val.
endform.                    “ f_fill_cell
*———————————————————————-*
* FORM f_download_full.
* –> Fill excel cell with data from internal table
*———————————————————————-*
FORM f_download_full.
  LOOP AT i_mara INTO x_mara.
    v_row = v_row + 1.
    PERFORM f_fill_cell USING v_row 1 x_mara-matnr.
    PERFORM f_fill_cell USING v_row 2 x_mara-ersda.
    PERFORM f_fill_cell USING v_row 3 x_mara-ernam.
    PERFORM f_fill_cell USING v_row 4 x_mara-laeda.
    PERFORM f_fill_cell USING v_row 5 x_mara-aenam.
  ENDLOOP.
ENDFORM.

PROCEDURE imp_excel IS excel ole2.obj_type; books ole2.obj_type; book ole2.obj_type; -- sheets ole2.obj_type; sheet ole2.obj_type; cell ole2.obj_type; args ole2.list_type; appli ole2.obj_type; filename varchar2(250); v1 varchar2(100); v2 varchar2(100); v3 varchar2(100); v4 varchar2(100); v5 number; v6 varchar2(100); v7 varchar2(100); v8 varchar2(100); v9 number; v10 number; m number(4); n number(2); cnt number; seq number; v_seq number; BEGIN appli:=ole2.create_obj('excel.application'); books:=ole2.get_obj_property(appli,'workbooks'); args:=ole2.create_arglist; filename := get_file_name(); ole2.add_arg(args,filename); book:=ole2.get_obj_property(books,'open',args); ole2.destroy_arglist(args); ole2.invoke(book,'activate'); --n:=1; --m:=2; n := :basic.n; m := :basic.m; loop args:=ole2.create_arglist; ole2.add_arg(args,n); sheet:=ole2.get_obj_property(book,'worksheets',args); ole2.destroy_arglist(args); m:=m+1; args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,2); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v2:=ltrim(rtrim(ole2.get_char_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,3); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v3:=ltrim(rtrim(ole2.get_char_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,4); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v4:=ltrim(rtrim(ole2.get_char_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,5); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v5:=ltrim(rtrim(ole2.get_num_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,6); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v6:=ltrim(rtrim(ole2.get_char_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,7); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v7:=ltrim(rtrim(ole2.get_char_property(cell,'value')),''''); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,8); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v8:=ltrim(rtrim(ole2.get_char_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,9); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v9:=ltrim(rtrim(ole2.get_num_property(cell,'value'))); ole2.release_obj(cell); args:=ole2.create_arglist; ole2.add_arg(args,m); ole2.add_arg(args,10); cell:=ole2.get_obj_property(sheet,'cells',args); ole2.destroy_arglist(args); v10:=ltrim(rtrim(ole2.get_num_property(cell,'value'))); ole2.release_obj(cell); exit when v7 is null; --message_alert('v2='||v2||';v3='||v3||';v4='||v4||';v5='||v5||';v6='||v6||';v7='||v7||';v8='||v8||';v9='||v9||';v10='||v10); begin update hr0c0 set hr0c0_sim_name = v2, hr0c0_sex = v3, hr0c0_nation = v4, hr0c0_birthday = to_date(to_char(v5),'yyyymmdd'), hr0c0_address = v6, hr0c0_organ = v8, hr0c0_issue_date = to_date('19000101','yyyymmdd') + v9 - 2, hr0c0_expire_date = to_date('19000101','yyyymmdd') + v10 - 2 where hr0c0_id_no = v7; if sql%notfound then insert into hr0c0 (hr0c0_sim_name,hr0c0_sex,hr0c0_nation,hr0c0_birthday, hr0c0_address,hr0c0_id_no,hr0c0_organ, hr0c0_issue_date,hr0c0_expire_date) values (v2,v3,v4,to_date(to_char(v5),'yyyymmdd'),v6,v7,v8, to_date('19000101','yyyymmdd') + v9 - 2, to_date('19000101','yyyymmdd') + v10 - 2); end if; exception when others then message_alert(dbms_error_text); raise form_trigger_failure; end; end loop; ole2.release_obj(sheet); ole2.invoke(book,'Close'); ole2.release_obj(book); ole2.release_obj(books); ole2.invoke(excel,'Quit'); ole2.release_obj(appli); if form_success then :system.message_level := 20; commit; :system.message_level := 0; message_alert('眒絳'||(m-2)||'捩旯爺痐訧蹋'); else ole2.release_obj(sheet); ole2.invoke(book,'Close'); ole2.release_obj(book); ole2.release_obj(books); ole2.invoke(excel,'Quit'); ole2.release_obj(appli); rollback; message_alert('絳訧蹋堤渣,'||dbms_error_text); raise form_trigger_failure; end if; END;
最新发布
12-10
你提供的代码是一段 **Oracle Forms**(通常称为 Form Builder 或 Oracle Developer Suite)中使用 **OLE2** 包调用 Microsoft Excel 的 PL/SQL 存储过程,用于从 Excel 文件导入数据到数据库表 `hr0c0` 中。这段代码逻辑清晰但存在一些可优化和潜在问题的地方。 --- ### ✅ 功能概述 该存储过程 `imp_excel` 实现了以下功能: 1. 使用 OLE 自动化启动 Excel 应用。 2. 打开用户选择的 Excel 文件。 3. 读取指定工作表(由参数 `:basic.n` 控制)中的数据行(从第 `m+1` 行开始)。 4. 每次读取第 2~10 列的数据: - 姓名、性别、民族、生日(YYYYMMDD 格式)、地址、身份证号、签发机关、发证日期、有效期截止日 5. 将生日和日期字段进行转换(Excel 日期以 1900-01-01 为基准 + 数值偏移) 6. 更新或插入 `hr0c0` 表 7. 遇到身份证号为空时退出循环 8. 关闭 Excel 并提交事务 --- ## 🔍 逐段分析与改进建议 ```plsql PROCEDURE imp_excel IS excel ole2.obj_type; books ole2.obj_type; book ole2.obj_type; sheet ole2.obj_type; cell ole2.obj_type; args ole2.list_type; appli ole2.obj_type; filename varchar2(250); v1 varchar2(100); -- 未使用 v2 varchar2(100); -- 姓名 v3 varchar2(100); -- 性别 v4 varchar2(100); -- 民族 v5 number; -- 生日(数值 YYYYMMDD) v6 varchar2(100); -- 地址 v7 varchar2(100); -- 身份证号 v8 varchar2(100); -- 签发机关 v9 number; -- 发证日期(Excel 序列数) v10 number; -- 失效日期(Excel 序列数) m number(4); n number(2); cnt number; seq number; v_seq number; BEGIN ``` > ✅ 变量声明正常,建议去掉未使用的变量如 `v1`, `cnt`, `seq`, `v_seq`。 --- ### 1. 启动 Excel 并打开文件 ```plsql appli := ole2.create_obj('excel.application'); books := ole2.get_obj_property(appli, 'workbooks'); args := ole2.create_arglist; filename := get_file_name(); -- 弹出文件选择对话框 ole2.add_arg(args, filename); book := ole2.get_obj_property(books, 'open', args); ole2.destroy_arglist(args); ole2.invoke(book, 'activate'); ``` > ⚠️ 注意事项: - `get_file_name()` 是自定义函数,需确保其返回合法路径。 - 此处没有设置 `appli.visible := false;`,建议隐藏 Excel 窗口避免干扰用户。 ✅ 改进建议添加: ```plsql ole2.set_property(appli, 'Visible', 'false'); -- 不显示 Excel 界面 ``` --- ### 2. 获取工作表索引和起始行 ```plsql n := :basic.n; -- 工作表编号 m := :basic.m; -- 起始行(数据从 m+1 开始读) ``` > ✅ 合理设计,但应验证输入有效性。 --- ### 3. 主循环:逐行读取 Excel 数据 #### 示例片段(仅展示前两列): ```plsql loop args := ole2.create_arglist; ole2.add_arg(args, n); sheet := ole2.get_obj_property(book, 'worksheets', args); ole2.destroy_arglist(args); m := m + 1; args := ole2.create_arglist; ole2.add_arg(args, m); ole2.add_arg(args, 2); cell := ole2.get_obj_property(sheet, 'cells', args); ole2.destroy_arglist(args); v2 := ltrim(rtrim(ole2.get_char_property(cell, 'value'))); ole2.release_obj(cell); ``` > ❌ 问题点: - **每次循环都重新获取 worksheet**,效率极低!应该在循环外获取一次。 - 多次创建 `args` 和 `cell` 对象,容易造成内存泄漏(尤其失败时未释放)。 - `ltrim(rtrim(...))` 是合理的去空格操作。 - `get_char_property` / `get_num_property` 使用正确。 ✅ 改进方案:将 `sheet` 提取到循环外 ```plsql args := ole2.create_arglist; ole2.add_arg(args, n); sheet := ole2.get_obj_property(book, 'worksheets', args); ole2.destroy_arglist(args); loop m := m + 1; -- 读取各列... ``` --- ### 4. Excel 日期转换说明 ```plsql hr0c0_birthday = to_date(to_char(v5),'yyyymmdd') -- 直接字符串转日期 hr0c0_issue_date = to_date('19000101','yyyymmdd') + v9 - 2 ``` > 📌 重要背景知识: - Excel 中日期是“自 1900-01-01 起的天数”,但有一个著名 bug:它错误地认为 1900 年是闰年(有 2 月 29 日),所以所有日期都多算了一天。 - 因此 `+ v9 - 2` 是为了修正这个偏差(1900-01-01 是第 1 天 → 实际加 `v9 - 2` 才准确) ✅ 写法正确! --- ### 5. 插入/更新逻辑 ```plsql update hr0c0 set ... where hr0c0_id_no = v7; if sql%notfound then insert into hr0c0 values (...); end if; ``` > ✅ 标准的“先更后插”模式,适用于主键唯一场景。 ⚠️ 但注意: - 如果 `hr0c0_id_no` 不是主键或唯一索引,可能重复插入。 - 推荐使用 `MERGE INTO` 更高效安全。 ✅ 替代写法(推荐): ```plsql MERGE INTO hr0c0 t USING (SELECT v2 as name, v3 as sex, v4 as nation, to_date(to_char(v5), 'yyyymmdd') as birth, v6 as addr, v7 as idno, v8 as organ, to_date('19000101', 'yyyymmdd') + v9 - 2 as issue, to_date('19000101', 'yyyymmdd') + v10 - 2 as expire FROM dual) s ON (t.hr0c0_id_no = s.idno) WHEN MATCHED THEN UPDATE SET t.hr0c0_sim_name = s.name, t.hr0c0_sex = s.sex, ... WHEN NOT MATCHED THEN INSERT VALUES (s.name, s.sex, ..., s.expire); ``` --- ### 6. 异常处理与资源释放 ```plsql exception when others then message_alert(dbms_error_text); raise form_trigger_failure; ``` > ⚠️ 严重问题:异常发生时未释放 OLE 对象!会导致 Excel 进程残留! ✅ 必须在异常块中也释放对象! 建议封装清理逻辑为子程序或使用 `goto` 跳转至统一出口。 --- ### 7. 成功与失败后的关闭操作 ```plsql if form_success then commit; message_alert('导入成功' || (m-2) || '条记录'); else rollback; message_alert('导入失败,'||dbms_error_text); raise form_trigger_failure; end if; ``` > ✅ 逻辑合理,但 `form_success` 是 Forms 内部变量吗?请确认是否可用。 实际中建议用布尔标志跟踪状态。 --- ## ✅ 改进后的完整版本(精简+健壮) ```plsql PROCEDURE imp_excel IS appli ole2.obj_type; books ole2.obj_type; book ole2.obj_type; sheet ole2.obj_type; cell ole2.obj_type; args ole2.list_type; filename varchar2(500); v2 varchar2(100); v3 varchar2(100); v4 varchar2(100); v5 number; v6 varchar2(100); v7 varchar2(100); v8 varchar2(100); v9 number; v10 number; m number := :basic.m; n number := :basic.n; success BOOLEAN := TRUE; -- 辅助过程:释放对象 PROCEDURE free_obj(obj IN OUT ole2.obj_type) IS BEGIN IF obj IS NOT NULL THEN ole2.release_obj(obj); obj := NULL; END IF; END; BEGIN -- 初始化 Excel appli := ole2.create_obj('Excel.Application'); ole2.set_property(appli, 'Visible', 'false'); books := ole2.get_obj_property(appli, 'Workbooks'); args := ole2.create_arglist; filename := get_file_name(); IF filename IS NULL THEN message_alert('未选择文件'); GOTO cleanup; END IF; ole2.add_arg(args, filename); book := ole2.get_obj_property(books, 'Open', args); ole2.destroy_arglist(args); args := ole2.create_arglist; ole2.add_arg(args, n); sheet := ole2.get_obj_property(book, 'Worksheets', args); ole2.destroy_arglist(args); -- 主循环读取数据 LOOP m := m + 1; -- 读取第 m 行第 2 列(B列) args := ole2.create_arglist; ole2.add_arg(args, m); ole2.add_arg(args, 2); cell := ole2.get_obj_property(sheet, 'Cells', args); ole2.destroy_arglist(args); v2 := trim(ole2.get_char_property(cell, 'Value')); free_obj(cell); -- 第3列 C args := ole2.create_arglist; ole2.add_arg(args, m); ole2.add_arg(args, 3); cell := ole2.get_obj_property(sheet, 'Cells', args); ole2.destroy_arglist(args); v3 := trim(ole2.get_char_property(cell, 'Value')); free_obj(cell); -- 继续其他列...此处省略,结构相同 -- 第7列(身份证号)判断是否结束 EXIT WHEN v7 IS NULL OR LENGTH(v7) < 15; -- MERGE 操作 MERGE INTO hr0c0 t USING (SELECT v2 as name, v3 as sex, v4 as nation, TO_DATE(TO_CHAR(v5), 'YYYYMMDD') as birth, v6 as addr, v7 as idno, v8 as organ, TO_DATE('19000101', 'YYYYMMDD') + v9 - 2 as issue, TO_DATE('19000101', 'YYYYMMDD') + v10 - 2 as expire FROM dual) s ON (t.hr0c0_id_no = s.idno) WHEN MATCHED THEN UPDATE SET t.hr0c0_sim_name = s.name, t.hr0c0_sex = s.sex, t.hr0c0_nation = s.nation, t.hr0c0_birthday = s.birth, t.hr0c0_address = s.addr, t.hr0c0_organ = s.organ, t.hr0c0_issue_date = s.issue, t.hr0c0_expire_date = s.expire WHEN NOT MATCHED THEN INSERT VALUES ( s.name, s.sex, s.nation, s.birth, s.addr, s.idno, s.organ, s.issue, s.expire ); END LOOP; COMMIT; message_alert('成功导入 ' || (m - :basic.m - 1) || ' 条记录'); -- 清理资源 <<cleanup>> free_obj(sheet); IF book IS NOT NULL THEN ole2.invoke(book, 'Close'); free_obj(book); END IF; free_obj(books); ole2.invoke(appli, 'Quit'); free_obj(appli); EXCEPTION WHEN OTHERS THEN ROLLBACK; message_alert('导入失败: ' || SQLERRM); free_obj(sheet); IF book IS NOT NULL THEN ole2.invoke(book, 'Close'); free_obj(book); END IF; free_obj(books); ole2.invoke(appli, 'Quit'); free_obj(appli); RAISE FORM_TRIGGER_FAILURE; END imp_excel; ``` --- ## ✅ 解释总结 | 特性 | 说明 | |------|------| | `OLE2` | Oracle Forms 提供的 OLE 自动化接口,用于控制外部 COM 组件(如 Excel) | | `get_file_name()` | 自定义函数,弹出文件选择框,返回完整路径 | | Excel 日期处理 | Excel 序列号从 1900-01-01 开始,需 `-2` 修正闰年 bug | | `MERGE INTO` | 推荐替代 `UPDATE + INSERT`,原子性强、性能好 | | 资源管理 | 必须 `release_obj`,否则 Excel 进程不退出 | | 错误处理 | 异常必须包含资源清理,防止内存泄露 | --- ##
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值