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

时间:2017-08-03 07:56:23

标签: 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 => ?));

此查询返回类似这样的内容(以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

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

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

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

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

我已经尝试过研究:

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

3 个答案:

答案 0 :(得分:1)

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

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

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

我使用DataTables.js jQuery插件来实现这种方法。经过一周的技术估算和学习一些基本的JavaScript(这不是我的主要技能),我有以下几点:

在APEX应用程序中,我实现了一个Ajax Callback过程(称为TEST_AJAX),它运行PL / SQL代码,它将JSON对象返回SYS.HTP输出(使用APEX_JSON或{ {1}}包)。来源:

HTP

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; 功能来源:

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;

我将DataTables.js的CSS和JS文件上传到应用程序静态文件,并将它们包含在页面属性中。在<div id="datatable_test_container"></div> 页面的JavaScript部分,我添加了此javascript代码:

Function and Global Variable Declaration

对于var $ = apex.jQuery; var table; var columns; var rows; //table initialization function function table_init(json_data) { return $('#datatable_test').DataTable({ //column defaults options columnDefs: [{ "data": null, "defaultContent": "-", "targets": "_all" }], columns: json_data.columns, data: json_data.data, stateSave: true }); } //function to asynchronously get data from APEX AJAX CALLBACK //process and then to draw a table based on this data function worker() { //run the process called TEST_JSON apex.server.process( "TEST_JSON", {}, { success: function(pData) { //on first run we need to initialize the table if (typeof table == 'undefined') { //save current data for future use columns = $.extend(true, [], pData.columns); rows = $.extend(true, [], pData.data); //generate empty html-table in the container $('#datatable_test_container').append('<table id = "datatable_test" class = "display" cellspacing = "0" width = "100%" > < /table>'); //init the table table = table_init(pData); //when columns of the table changes we need to //reinitialize the table (DataTables require it due to architecture) } else if (JSON.stringify(columns) != JSON.stringify(pData.columns)) { //save current data for future use columns = $.extend(true, [], pData.columns); rows = $.extend(true, [], pData.data); //delete the table from DOM table.destroy(true); //generate empty html-table in the container $('#datatable_test_container').append('<table id = "datatable_test" class = "display" cellspacing = "0" width = "100%" > < /table>'); //reinit the table table = table_init(pData); } //if data changes, clear and re-draw the table else if (JSON.stringify(rows) != JSON.stringify(pData.data)) { //save current data for future use //we don't need to save the columns, they didn't change rows = $.extend(true, [], pData.data); //clear table, add rows from recieved JSON-object, re- draw the table with new data table.clear().rows.add(pData.data).draw(false); } //if nothing changes, we do nothing } } ); //repeat the procedure in a second setTimeout(worker, 1000); }; 我添加了:

Execute when Page Loads

所有这一切:

  1. 静态内容区域中的静态$(document).ready(function() { worker(); }); 收到空 应用DataTables构造函数的表。
  2. JavaScript代码通过触发Ajax Callback服务器开始工作 进程,并在成功时使用此过程返回的结果。
  3. 例如,DataTables构造函数支持不同类型的数据源 它可以解析html表或进行ajax调用,但是我 首选使用APEX流程,然后将表基于 JSON-object,此进程返回。
  4. 然后剧本观察变化。如果列更改,则删除该表 从文档中重新初始化并使用新数据,如果只有行更改, 然后用这些数据重新绘制表格。如果数据没有任何变化 然后脚本什么都不做。
  5. 此过程每秒重复一次。
  6. 因此,有一个完全交互式的,动态刷新的报告,包括排序,分页,搜索,事件处理等选项。所有这些都是在客户端完成的,无需对服务器进行额外查询。

    您可以使用此live demo检查结果(顶部区域是DataTables报告,在它下面是源表上的可编辑交互式网格,要查看更改,您可以使用交互式网格更改数据)。

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

    更新05.09.2017:添加了<div> Ajax回调流程和APEX_JSON PL / SQL功能的列表。

答案 1 :(得分:-1)

你是对的。我只根据我对你的要求描述的早期理解提供了一个意见。现在我更仔细地阅读,我改变了主意,因为我意识到你所拥有的并不是一个困难的矩阵。事实上我有很多这些。 。

我本身并不了解最佳做法,但我可以与您分享我在类似要求方面所做的工作:

对于基于矩阵的报告,我更喜欢经典报告,我将过滤条件放在标题部分(就像在模拟中一样),并根据用户选择,信息变化非常好。这就是我处理过滤和排序的方式。

满足您要求的最难的部分(IMO)是为了导出目的检查单元格。您应该能够在查询中动态启用切换控件,并且使用一些AJAX,您应该能够拾取所选择的出口。

答案 2 :(得分:-4)

根据您的要求,APEX可能不是正确的工具,您将受到JQuery的底层库和功能的无限限制等。

我不打算在APEX上开发这样的应用程序,我认为对APEX提供的技术的要求太过分了。

另一方面,你谈论数据库性能..很好,你在规划阶段考虑到了这一点,但很糟糕,因为你已经限制了自己......有选择......例如获取数据库中的内存中选项和/从实体化视图中缓存结果。将表固定到内存中。