Oracle EBS ERP开发——报表生成Excel标准模板设计

在Oracle EBS ERP系统中,报表生成是业务数据展示和分析的重要环节。为了保证报表输出的规范性和易维护性,设计一个标准的Excel报表模板非常关键。本文将分享一个通用的Excel报表生成标准模板设计思路,并预留模板存放位置,支持多种字符输出格式,包括HTML格式、日志格式等。

一、设计目标

统一模板管理:所有报表模板集中存放,方便维护和版本控制。

多格式支持:支持Excel、HTML、日志等多种输出格式,满足不同业务需求。

字符格式灵活:支持不同类型的字符输出格式,如文本、数字、日期、HTML标签等。

易扩展性:模板结构清晰,方便后续功能扩展和定制。

二、模板存放位置设计

建议在Oracle EBS的文件系统中,预留一个专门的目录用于存放报表模板文件,例如:

/u01/app/oracle/ebs_reports/templates/

该目录下可以根据报表类型或业务模块建立子目录,例如:

```

/u01/app/oracle/ebs_reports/templates/order/

/u01/app/oracle/ebs_reports/templates/inventory/

```

模板文件可以采用Excel的`.xlsx`格式,也可以是HTML模板文件,方便不同格式的输出。

三、标准模板结构设计

  1. Excel模板结构
  • **标题行**:报表名称、生成日期、制作者等信息。

  • **表头行**:列名,支持多行表头。

  • **数据区域**:动态填充业务数据。

  • **汇总行**:合计、平均等统计信息。

  • **格式定义**:字体、颜色、边框、数字格式等。

  1. HTML输出格式

在模板中预留HTML标签支持,例如:

```html

<td><b>#COLUMN_NAME#</b></td>

```

通过替换`#COLUMN_NAME#`实现动态内容填充,支持加粗、颜色、超链接等格式。

  1. 日志输出格式

日志格式通常为纯文本,模板中定义日志行格式,例如:

```

INFO #TIMESTAMP# - #MESSAGE#

```

通过替换`#TIMESTAMP#`和`#MESSAGE#`实现日志内容输出。

四、字符输出格式设计

| 输出格式类型 | 说明 | 示例 |

|--------------|--------------------------------|---------------------------------|

| 普通文本 | 直接输出字符串 | "订单编号" |

| 数字格式 | 支持千分位、两位小数等格式 | 12345.67 → 12,345.67 |

| 日期格式 | 支持多种日期格式输出 | 2024-06-01 → 2024年06月01日 |

| HTML格式 | 支持HTML标签包裹,丰富展示效果 | `<b>重要</b>` |

| 日志格式 | 纯文本格式,带时间戳和日志级别 | `ERROR 2024-06-01 10:00:00 - 错误信息` |

五、示例:PL/SQL调用模板生成Excel报表

```plsql

sql 复制代码
create or replace package body CUX_XXX_PKG is

  PROCEDURE main(errbuf            OUT VARCHAR2,
                                  retcode           OUT VARCHAR2,
                                  p_organization_id IN NUMBER,
                                  p_date_from       IN DATE,
                                  p_date_to         IN DATE,
                                  p_param1          IN VARCHAR2 DEFAULT NULL,
                                  p_param2          IN VARCHAR2 DEFAULT NULL,
                                  p_param3          IN VARCHAR2 DEFAULT NULL) IS 

    -- 1. 变量声明区域
    -- 性能监控变量
    v_start_time TIMESTAMP;
    v_end_time TIMESTAMP;
    v_elapsed_seconds NUMBER;
    v_total_records NUMBER := 0;
    
    -- 业务变量
    v_field1_value VARCHAR2(100);
    v_field2_value VARCHAR2(100);
    v_field3_value VARCHAR2(100);
    
    -- 时间统计变量
    v_loop_start_time TIMESTAMP;
    v_loop_end_time TIMESTAMP;
    v_loop_total_seconds NUMBER := 0;
    
    -- 2. 游标定义区域
    -- 核心游标:根据业务需求修改SELECT语句
    CURSOR cur_main_data IS
      SELECT 
          table1.field1 AS 字段1,
          table1.field2 AS 字段2,
          table2.field3 AS 字段3,
          table1.creation_date AS 创建日期,
          -- 可以根据需要添加更多字段
          MAX(aux_table.aux_field) AS 辅助字段  -- 使用聚合函数减少后续查询
      FROM main_table table1,
           related_table table2,
           auxiliary_table aux_table
     WHERE table1.org_id = p_organization_id
       AND table1.creation_date BETWEEN nvl(p_date_from, table1.creation_date) 
                                    AND nvl(trunc(p_date_to) + 0.99999, table1.creation_date)
       AND table1.related_id = table2.related_id(+)
       AND table1.key_field = aux_table.key_field(+)
       AND (p_param1 IS NULL OR table1.field1 = p_param1)
       AND (p_param2 IS NULL OR table2.field2 LIKE '%' || p_param2 || '%')
       AND (p_param3 IS NULL OR table1.field3 = p_param3)
     GROUP BY table1.field1,
              table1.field2,
              table2.field3,
              table1.creation_date
     ORDER BY table1.creation_date,
              table1.field1;

    -- 3. 辅助变量
    l_auxiliary_data NUMBER;

  BEGIN
      -- ====================================================
      -- 步骤1: 程序初始化 - 性能监控开始
      -- ====================================================
      v_start_time := SYSTIMESTAMP;
      
      -- 打印程序开始日志
      fnd_file.put_line(fnd_file.log, '  _____   ____    _____ ');
      fnd_file.put_line(fnd_file.log, ' | ____| |  _ \  | ____|');
      fnd_file.put_line(fnd_file.log, ' |  _|   | |_) | |  _|  ');
      fnd_file.put_line(fnd_file.log, ' | |___  |  __/  | |___ ');
      fnd_file.put_line(fnd_file.log, ' |_____| |_|     |_____|');
      fnd_file.put_line(fnd_file.log, '======================================================');
      fnd_file.put_line(fnd_file.log, '          通用报表模板程序开始执行                      ');
      fnd_file.put_line(fnd_file.log, '开始时间: ' || TO_CHAR(v_start_time, 'YYYY-MM-DD HH24:MI:SS.FF6'));
      fnd_file.put_line(fnd_file.log, '======================================================');

      -- 打印输入参数
      fnd_file.put_line(fnd_file.log, '输入参数信息:');
      fnd_file.put_line(fnd_file.log, '  组织ID: ' || NVL(TO_CHAR(p_organization_id), 'NULL'));
      fnd_file.put_line(fnd_file.log, '  日期从: ' || NVL(TO_CHAR(p_date_from, 'YYYY-MM-DD'), 'NULL'));
      fnd_file.put_line(fnd_file.log, '  日期到: ' || NVL(TO_CHAR(p_date_to, 'YYYY-MM-DD'), 'NULL'));
      fnd_file.put_line(fnd_file.log, '  参数1: ' || NVL(p_param1, 'NULL'));
      fnd_file.put_line(fnd_file.log, '  参数2: ' || NVL(p_param2, 'NULL'));
      fnd_file.put_line(fnd_file.log, '  参数3: ' || NVL(p_param3, 'NULL'));
      fnd_file.put_line(fnd_file.log, '----------------------------------------');

      -- ====================================================
      -- 步骤2: 生成HTML表头
      -- ====================================================
      fnd_file.put_line(fnd_file.log, '步骤1: 开始生成HTML表头');
      
      -- HTML表头生成
      fnd_file.put_line(2, '<html>');
      fnd_file.put_line(2, '<head>');
      fnd_file.put_line(2, '<style TYPE="text/css">');
      fnd_file.put_line(2, 'BODY { FONT-SIZE: 9pt}');
      fnd_file.put_line(2, 'TH   { FONT-SIZE: 9pt}');
      fnd_file.put_line(2, 'TD   { FONT-SIZE: 9pt}');
      fnd_file.put_line(2, '</STYLE>');
      fnd_file.put_line(2, '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">');
      fnd_file.put_line(2, '<title>通用报表模板</title>');
      fnd_file.put_line(2, '</head>');
      fnd_file.put_line(2, '<body>');
      fnd_file.put_line(2, '<table border="0" width="100%" cellspacing="3" cellpadding="3">');
      fnd_file.put_line(2, '<tr>');
      fnd_file.put_line(2, '<td colspan="6"><font size="5"><div align="center"><strong>通用报表模板</strong></div></font></td>');
      fnd_file.put_line(2, '</tr>');
      fnd_file.put_line(2, '</table>');
      fnd_file.put_line(2, '<table width="100%" border="1" cellspacing="0" cellpadding="0">');
      
      -- 表格列头 - 根据实际字段修改
      fnd_file.put_line(2, '<tr bgColor=#D6DCE4>');
      fnd_file.put_line(2, '<td width="10%"><div align="center"><font size="2">字段1</font></div></td>');
      fnd_file.put_line(2, '<td width="15%"><div align="center"><font size="2">字段2</font></div></td>');
      fnd_file.put_line(2, '<td width="15%"><div align="center"><font size="2">字段3</font></div></td>');
      fnd_file.put_line(2, '<td width="10%"><div align="center"><font size="2">创建日期</font></div></td>');
      fnd_file.put_line(2, '<td width="10%"><div align="center"><font size="2">辅助字段</font></div></td>');
      fnd_file.put_line(2, '<td width="10%"><div align="center"><font size="2">计算字段</font></div></td>');
      fnd_file.put_line(2, '</tr>');

      -- ====================================================
      -- 步骤3: 清理临时表(如果需要)
      -- ====================================================
      fnd_file.put_line(fnd_file.log, '步骤2: 清理临时表(如需要)');
      -- DELETE FROM your_temp_table;
      -- COMMIT;

      -- ====================================================
      -- 步骤4: 执行游标查询和循环处理
      -- ====================================================
      fnd_file.put_line(fnd_file.log, '步骤3: 开始执行游标查询和循环处理');
      v_loop_start_time := SYSTIMESTAMP;
      
      -- 初始化累计变量
      v_total_records := 0;

      -- 核心循环:处理每一行数据
      FOR c IN cur_main_data LOOP
          v_total_records := v_total_records + 1;

          -- 进度显示
          IF MOD(v_total_records, 1000) = 0 THEN
              fnd_file.put_line(fnd_file.log, '  已处理 ' || v_total_records || ' 条记录');
          END IF;  
          
          -- ====================================================
          -- 步骤4.1: 辅助数据查询(根据需要)
          -- ====================================================
          -- 示例:查询相关数据
          BEGIN
              SELECT auxiliary_value 
                INTO l_auxiliary_data
                FROM another_table 
               WHERE key_field = c.字段1;
          EXCEPTION
              WHEN NO_DATA_FOUND THEN
                  l_auxiliary_data := 0;
              WHEN OTHERS THEN
                  l_auxiliary_data := NULL;
          END;

          -- ====================================================
          -- 步骤4.2: 字段值处理
          -- ====================================================
          -- 示例:字段值转换或计算
          v_field1_value := NVL(c.字段1, '空值');
          v_field2_value := SUBSTR(c.字段2, 1, 50); -- 截断长文本
          v_field3_value := c.字段3;

          -- ====================================================
          -- 步骤4.3: 生成HTML表格行
          -- ====================================================
          fnd_file.put_line(2, '<tr>');
          fnd_file.put_line(2, '<td style="vnd.ms-excel.numberformat:@"><div align="center"><font size="2">' || 
                          v_field1_value || '</font></div></td>');
          fnd_file.put_line(2, '<td><div align="center"><font size="2">' || 
                          v_field2_value || '</font></div></td>');
          fnd_file.put_line(2, '<td><div align="center"><font size="2">' || 
                          v_field3_value || '</font></div></td>');
          fnd_file.put_line(2, '<td><div align="center"><font size="2">' || 
                          TO_CHAR(c.创建日期, 'YYYY-MM-DD') || '</font></div></td>');
          fnd_file.put_line(2, '<td><div align="center"><font size="2">' || 
                          NVL(c.辅助字段, '&nbsp;') || '</font></div></td>');
          fnd_file.put_line(2, '<td><div align="right"><font size="2">' || 
                          NVL(TO_CHAR(l_auxiliary_data), '&nbsp;') || '</font></div></td>');
          fnd_file.put_line(2, '</tr>');
      
      END LOOP; -- 结束核心循环
      
      v_loop_end_time := SYSTIMESTAMP;
      v_loop_total_seconds := (EXTRACT(SECOND FROM (v_loop_end_time - v_loop_start_time)) + 
                              EXTRACT(MINUTE FROM (v_loop_end_time - v_loop_start_time)) * 60 +
                              EXTRACT(HOUR FROM (v_loop_end_time - v_loop_start_time)) * 3600 +
                              EXTRACT(DAY FROM (v_loop_end_time - v_loop_start_time)) * 86400);

      -- ====================================================
      -- 步骤5: HTML结束标签
      -- ====================================================
      fnd_file.put_line(2, '</table>');
      fnd_file.put_line(2, '</body>');
      fnd_file.put_line(2, '</html>');
                        
      -- ====================================================
      -- 步骤6: 性能分析总结
      -- ====================================================
      v_end_time := SYSTIMESTAMP;
      v_elapsed_seconds := (EXTRACT(SECOND FROM (v_end_time - v_start_time)) + 
                           EXTRACT(MINUTE FROM (v_end_time - v_start_time)) * 60 +
                           EXTRACT(HOUR FROM (v_end_time - v_start_time)) * 3600 +
                           EXTRACT(DAY FROM (v_end_time - v_start_time)) * 86400);

      fnd_file.put_line(fnd_file.log, '====================================================');
      fnd_file.put_line(fnd_file.log, '执行总结:');
      fnd_file.put_line(fnd_file.log, '  总记录数: ' || v_total_records);
      fnd_file.put_line(fnd_file.log, '  总执行时间: ' || ROUND(v_elapsed_seconds, 6) || ' 秒');
      fnd_file.put_line(fnd_file.log, '  循环处理时间: ' || ROUND(v_loop_total_seconds, 6) || ' 秒');
      fnd_file.put_line(fnd_file.log, '  平均每行处理时间: ' || 
          CASE WHEN v_total_records > 0 THEN ROUND(v_loop_total_seconds / v_total_records * 1000, 4) ELSE 0 END || ' 毫秒');
      fnd_file.put_line(fnd_file.log, '结束时间: ' || TO_CHAR(v_end_time, 'YYYY-MM-DD HH24:MI:SS.FF6'));
      fnd_file.put_line(fnd_file.log, '====================================================');
      
      -- 设置成功返回代码
      errbuf := NULL;
      retcode := '0';
      
  EXCEPTION
      WHEN OTHERS THEN
          -- 异常处理
          errbuf := SQLERRM;
          retcode := '2';
          fnd_file.put_line(fnd_file.log, '程序执行异常: ' || SQLERRM);
          fnd_file.put_line(fnd_file.log, DBMS_UTILITY.format_error_backtrace);
  END main;

end CUX_XXX_PKG ;
相关推荐
浪客灿心1 小时前
项目篇:模块设计与实现
数据库·c++
流星白龙3 小时前
【MySQL高阶】26.事务(1)
数据库·mysql
三十..3 小时前
Redis 核心原理与高可用架构实践
运维·数据库·redis
这个DBA有点耶3 小时前
索引优化深潜(下):索引合并、ICP 与索引设计的实战法则
数据库·mysql·架构
努力努力再努力wz4 小时前
【内存管理与高并发内存池系列】从 mmap 到 malloc:文件映射、匿名映射与 glibc 内存分配机制详解
linux·c语言·数据结构·数据库·c++·qt·链表
JdSnE27zv4 小时前
Qt 操作SQLite数据库
数据库·qt·sqlite
tedcloud1234 小时前
HyperFrames部署教程:用HTML生成MP4视频
前端·数据库·人工智能·html·音视频
布朗克1684 小时前
25 IO流高级操作——序列化、NIO与Files工具类
java·数据库·io·nio
阿演4 小时前
DataDjinn 新版本更新:新增 Oracle 支持,查询窗口、表预览和连接树继续打磨
数据库·oracle·ai编程·数据库连接工具
lixora5 小时前
Oracle 11g Active Data Guard Go 自动化部署工具 v1.0
数据库·oracle