我需要使用 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”列的值对列进行排序)。
所以问题是 - 通过一些交互和自定义样式实现此类矩阵报告的最佳实践是什么?
我已经尝试研究的内容:
- 交互式报表数据透视功能 ( https://docs.oracle.com/cd/E71588_01/AEEUG/managing-pivot-reports.htm#AEEUG29137 ) - 缺乏自定义,对于许多值的处理效果不佳,尤其是当它们不是数字时。
- 基于函数的经典报告 - 我已实现 PL/SQL function它返回动态 PIVOT SQL 查询,在报表的属性中将
使用通用列名称
设置为是
(以便仅在运行时解析查询)以及标题报告我用了另一个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 就可以在这里执行任何操作。棘手的是这个解决方案非常复杂,我需要“从头开始”实现报告的所有逻辑,并且我相信有一种更好、更简单的方法来成功完成任务,但我不知道。<
最佳答案
不幸的是,由于报告的生存条件,我在问题中提到的选项都不满足所有要求:
- 数据应该每 5 秒动态更新一次。
- 报告的状态应在数据更新时保存。
- 报告的列数是可变的(列的定义是 提供数据),行数也是可变的。报告应有 排序、分页和滚动(按 X 和 Y)选项。所有的东西 (排序等)应该在客户端完成。
- 样式和自定义单元格渲染应应用于表格的单元格。
- 单元格应该是可点击的(点击应该生成一个事件,即 可拦截)。</li>
我意识到,对于这样的任务,最好在客户端即时操作 DOM,而不是使用一些开箱即用的 APEX 解决方案,例如经典报告、交互式报告或网格。
我用了DataTables.js这种方法的 jQuery 插件。经过一周的技术评估和学习一些基本的 JavaScript(这不是我的主要技能)后,我得到了以下结果:
在 APEX 应用程序中,我实现了一个 Ajax 回调进程(称为 TEST_AJAX
),它运行 PL/SQL 代码,该代码将 JSON 对象返回到 SYS.HTP
输出(使用APEX_JSON
或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;
然后我在页面上创建了一个静态内容区域,其来源如下:
<div id="datatable_test_container"></div>
我将 DataTables.js 的 CSS 和 JS 文件上传到应用程序静态文件,并将它们包含在页面属性中。在 JavaScript
Function and Global Variable Declaration
页面的一部分我添加了这段 JavaScript 代码:
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
我补充道:
$(document).ready(function() {
worker();
});
这一切的作用:
- 静态
<div>
在静态内容区域中收到一个空的 应用 DataTables 构造函数的表。 - JavaScript 代码通过触发 Ajax 回调服务器开始工作 处理,成功后使用此处理返回的结果。
- DataTables构造函数支持不同类型的数据源,例如 它可以解析 html 表或进行 ajax 调用,但我 首选使用 APEX 流程,然后将表格基于 JSON 对象,此过程返回。
- 然后脚本会监视更改。如果列发生更改,则表将被删除 从文档中并使用新数据重新初始化,如果只有行发生变化, 然后用这些数据重新绘制表格。如果数据没有变化 那么脚本什么也不做。
- 此过程每秒重复一次。
因此,有一个完全交互式、动态刷新的报告,具有排序、分页、搜索、事件处理等选项。所有这些都是在客户端完成的,无需向服务器进行额外的查询。
您可以使用此 live demo 检查结果(顶部区域是 DataTables 报告,其下方是源表上可编辑的交互式网格,要查看更改,您可以使用交互式网格更改数据)。
我不知道这是否是最好的方法,但它满足我的要求。
更新 05.09.2017: 添加了 APEX_JSON
的列表Ajax回调流程及go_pivot
PL/SQL 函数。
关于sql - 使用 APEX 实现动态矩阵报告的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45478150/