使用APEX实施动态矩阵报告的最佳方法是什么?

ZZa*_*ZZa 6 sql oracle plsql report oracle-apex

我需要使用Oracle Application Express框架完成此任务.

假设我们有这样一个问题:

select   
  col1,  
  col2,  
  val1,  
  val2,  
  val3,  
  val4,  
  val5,  
  val6,  
  val7,  
  val8,  
  val9,  
  val10,  
  val11  
from table(mega_function(city => ?, format => ?, percent => ?, days => ?));
Run Code Online (Sandbox Code Playgroud)

此查询返回类似这样的内容(以CSV格式显示):

col1;col2;val1;val2;val3;val4;val5;val6;val7;val8;val9;val10;val11
S2;C1;32000;120;"15:38:28";1450;120;1500;1200;31000;120;32600;300
S1;C1;28700;120;"15:35:01";150;120;1500;1800;2700;60;28900;120
S1;C2;27000;240;"14:44:23";0;1500;240;1200;25500;60;null;null
Run Code Online (Sandbox Code Playgroud)

简单来说,查询基于流水线函数,该函数接受一些参数并返回前两列不同值对的一些值集col1;col2.

我需要实现的是矩阵报告,其中值col1用作报告的行和值的col2列.在交叉点上有一些单元格,其中包含一对值的集合,并应用了一些格式和样式.还需要的是 - 按行排序(应按列"val1"的值对列进行排序).

或者如果我们在模型上显示上述需求: 小样

所以问题是 - 使用一些交互和自定义样式实现这样的矩阵报告的最佳实践是什么?

我已经尝试过研究:

  • 交互式报告数据透视功能(https://docs.oracle.com/cd/E71588_01/AEEUG/managing-pivot-reports.htm#AEEUG29137) - 缺乏自定义功能,对许多值都很有效,特别是当它们不是数字时.
  • 基于函数的经典报告 - 我已经实现了PL/SQL函数,该函数返回动态PIVOT SQL查询,在报告Use Generic Column Names集的属性中Yes(为了仅在运行时解析查询),对于报告的标题,我使用了另一个PL/SQL函数,以格式生成字符串heading1:headning2:...:headingN.该解决方案有效(你可以在这里查看 - https://apex.oracle.com/pls/apex/f?p=132832:2),但是我需要每次动态刷新报告,比方说,5秒,它会在性能方面很糟糕(如果我们谈论执行计划,动态SQL总是很糟糕而且不可管理).此解决方案也不合适,因为标题与数据不一致(实际上我order by col1在PL/SQL函数中的查询中使用以使标题位于其位置)并且我不知道如何在此处对行进行排序.
  • PL/SQL动态内容区域 - 我没有尝试在这里编写代码,但我意识到只使用HTP包和APEX API就可以做任何事情.最棘手的事情是这样的解决方案是相当复杂的,我需要实现"从头开始"报告所有的逻辑,我相信有一个更好,更简单的方法在任务,我不知道成功.

ZZa*_*ZZa 1

不幸的是,由于报告的生存条件,我在问题中提到的选项都不满足所有要求:

  • 数据应该每 5 秒动态更新一次。
  • 报告的状态应在数据更新时保存。
  • 报告的列数是可变的(列的定义由数据提供),行数也是可变的。报告应具有排序、分页和滚动(按 X 和 Y)选项。所有的事情(排序等)都应该在客户端完成。
  • 样式和自定义单元格渲染应应用于表格的单元格。
  • 单元格应该是可点击的(点击应该生成一个事件,该事件是可拦截的)。

我意识到,对于这样的任务,最好在客户端即时操作 DOM,而不是使用一些开箱即用的 APEX 解决方案,例如经典报告、交互式报告或网格。

我使用DataTables.js jQuery 插件来实现这种方法。经过一周的技术评估和学习一些基本的 JavaScript(这不是我的主要技能)后,我得到了以下结果:

在 APEX 应用程序中,我实现了一个 Ajax 回调进程(称为TEST_AJAX),它运行 PL/SQL 代码,该代码将 JSON 对象返回到SYS.HTP输出(使用APEX_JSONHTP包)。其来源:

declare 
    l_temp sys_refcursor;
begin  
    open l_temp for go_pivot;
    APEX_JSON.open_object;
    APEX_JSON.open_array('columns');
    APEX_JSON.open_object;
    APEX_JSON.write('data', 'COL2');
    APEX_JSON.write('title', '/');
    APEX_JSON.close_object;
    for x in (select distinct col1 from test order by 1) loop
        APEX_JSON.open_object;
        APEX_JSON.write('data', upper(x.col1));
        APEX_JSON.write('title', x.col1);
        APEX_JSON.close_object;
    end loop;
    APEX_JSON.close_array;
    APEX_JSON.write('data', l_temp); 
    APEX_JSON.close_object;
end;
Run Code Online (Sandbox Code Playgroud)

函数go_pivot源码:

create or replace function go_pivot return varchar2
  is
      l_query long := 'select col2';
  begin
      for x in (select distinct col1 from test order by col1)
      loop
          l_query := l_query ||
             replace(', min(decode(col1,''$X$'',v)) $X$',
                      '$X$',
                     x.col1);
     end loop;
     l_query := l_query || ' from test group by col2';
    return l_query;
 end;
Run Code Online (Sandbox Code Playgroud)

然后我在页面上创建了一个静态内容区域,其来源如下:

declare 
    l_temp sys_refcursor;
begin  
    open l_temp for go_pivot;
    APEX_JSON.open_object;
    APEX_JSON.open_array('columns');
    APEX_JSON.open_object;
    APEX_JSON.write('data', 'COL2');
    APEX_JSON.write('title', '/');
    APEX_JSON.close_object;
    for x in (select distinct col1 from test order by 1) loop
        APEX_JSON.open_object;
        APEX_JSON.write('data', upper(x.col1));
        APEX_JSON.write('title', x.col1);
        APEX_JSON.close_object;
    end loop;
    APEX_JSON.close_array;
    APEX_JSON.write('data', l_temp); 
    APEX_JSON.close_object;
end;
Run Code Online (Sandbox Code Playgroud)

我将 DataTables.js 的 CSS 和 JS 文件上传到应用程序静态文件,并将它们包含在页面属性中。在JavaScript页面的部分中Function and Global Variable Declaration,我添加了以下 JavaScript 代码:

create or replace function go_pivot return varchar2
  is
      l_query long := 'select col2';
  begin
      for x in (select distinct col1 from test order by col1)
      loop
          l_query := l_query ||
             replace(', min(decode(col1,''$X$'',v)) $X$',
                      '$X$',
                     x.col1);
     end loop;
     l_query := l_query || ' from test group by col2';
    return l_query;
 end;
Run Code Online (Sandbox Code Playgroud)

因为Execute when Page Loads我补充道:

<div id="datatable_test_container"></div>
Run Code Online (Sandbox Code Playgroud)

这一切的作用是什么:

  1. 静态内容区域中的Static<div>接收一个应用了 DataTables 构造函数的空表。
  2. JavaScript 代码通过触发 Ajax 回调服务器进程开始工作,并在成功时使用该进程返回的结果。
  3. DataTables 构造函数支持不同类型的数据源,例如,它可以解析 html 表或进行 ajax 调用,但我更喜欢使用 APEX 进程,然后将表基于此进程返回的 JSON 对象。
  4. 然后脚本会观察变化。如果列发生变化,则表格将从文档中删除并使用新数据重新初始化,如果仅行发生变化,则仅使用该数据重新绘制表格。如果数据没有发生任何变化,则脚本不会执行任何操作。
  5. 这个过程每秒都会重复一次。

因此,有一个完全交互式、动态刷新的报告,具有排序、分页、搜索、事件处理等选项。所有这些都是在客户端完成的,无需向服务器进行额外的查询。

您可以使用此实时演示检查结果 (顶部区域是 DataTables 报告,其下方的源表上有一个可编辑的交互式网格,要查看更改,您可以使用交互式网格更改数据)。

我不知道这是否是最好的方法,但它满足我的要求。

更新 05.09.2017:APEX_JSON添加了Ajax 回调过程和go_pivotPL/SQL 函数的列表。