Каковы наилучшие способы реализации динамического матричного отчета с помощью APEX?

мне нужно выполнить эту задачу с помощью Oracle Application Express framework.

допустим, у нас есть такой запрос:

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 что возвращает динамический сводный 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 Dynamic Content Region - я не пытался что-то здесь закодировать, но я понимаю, что здесь можно сделать все, что угодно, используя пакет HTP и APEX API. Хитрость в том, что это решение довольно сложное, мне нужно будет реализовать всю логику отчета "с нуля", и я считаю, что есть лучший и более простой способ добиться успеха в этой задаче, которого я не знаю.

3 ответов


к сожалению, ни один из вариантов, упомянутых в вопросе, не отвечал всем требованиям из-за условий, в которых будет жить отчет:

  • данные должны динамически обновляться каждые, скажем, 5 секунд.
  • состояние отчета должно быть сохранено поверх обновлений данных.
  • количество столбцов отчета является переменным (определение столбцов представлены данные), число строк переменной. Доклад должен иметь рассортировать, параметры разбиения на страницы и прокрутки (по X и Y). Все вещи (сортировка и т. д.) должно быть сделано на стороне клиента.
  • стили и пользовательский рендеринг ячеек должны применяться к ячейкам таблицы.
  • ячейки должны быть кликабельными (щелчок должен генерировать событие, которое interceptable).

я понял, что для такой задачи лучше манипулировать DOM на лету на стороне клиента, а не использовать некоторые нестандартные решения APEX, такие как классические отчеты, интерактивные отчеты или сетки.

я объекты DataTable.js плагин jQuery для этого подхода. После недели оценки технологии и изучения некоторого базового JavaScript (который не является моим основным навыком), у меня было следующее:

в приложении APEX я реализовал процесс обратного вызова Ajax (называемый TEST_AJAX), он запускает код PL / SQL, который возвращает JSON-object в 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>

я загрузил CSS и JS файлы DataTables.js к статическим файлам приложения и включил их в свойства страницы. В на странице 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 I добавлено:

$(document).ready(function() {
    worker();
});

что все это делает:

  1. статический <div> в области статического содержимого получает пустой таблица, в которой применяется конструктор DataTables.
  2. JavaScript-код начинает свою работу с запуска сервера обратного вызова Ajax process, а при успешном использовании результата этот процесс возвращается.
  3. конструктор DataTables поддерживает различные типы источников данных, например он может анализировать html-таблицу или сделайте ajax-вызов, но я предпочтительно использовать процесс APEX, а затем основывать таблицу на JSON-объект, который возвращает этот процесс.
  4. затем скрипт наблюдает за изменениями. При изменении столбцов таблица удаляется из документа и повторно инициализируется с использованием новых данных, если изменяется только строка, тогда таблица просто перерисовывается с этими данными. Если ничего не изменится в данных тогда сценарий ничего не делает.
  5. этот процесс повторяется каждую секунду.

как a в результате получается полностью интерактивный, динамически обновляемый отчет с такими параметрами, как сортировка, подкачка, поиск, обработка событий и так далее. И все это делается на стороне клиента без дополнительных запросов к серверу.

вы можете проверить результат с помощью этого live demo (верхняя область-отчет DataTables, под ним есть редактируемая интерактивная сетка в исходной таблице, чтобы увидеть изменения, вы можете изменить данные с помощью интерактивного сетка.)

я не знаю, является ли это лучшим подходом, но он отвечает моим требованиям.

обновлено 05.09.2017: добавлен список APEX_JSON процесс обратного вызова Ajax и go_pivot функция PL/SQL.


вы правы. Я только предоставил мнение, основанное на моем раннем понимании вашего описания требований.. Теперь, когда я прочитал это более внимательно, я передумал, потому что понял, что это не сложная матрица.. На самом деле у меня их много..

Я не знаю лучших практик как таковых, но я могу поделиться с вами тем, что я сделал с аналогичными требованиями:

для матричных отчетов я предпочитаю классические отчеты, я ставлю свои критерии фильтрации на раздел заголовка (как и в вашем макете) и на основе выбора пользователя информация меняется очень красиво. Вот как я управляюсь с фильтрацией и сортировкой.

самая сложная часть (IMO) с вашими требованиями-это проверка ячеек для целей экспорта.. Вы должны иметь возможность динамически включать элемент управления toggle в своем запросе, и с помощью AJAX вы должны иметь возможность выбирать выбранные для экспорта.


на основе ваших требований APEX, вероятно, не является правильным инструментом, вы будете бесконечно ограничены базовыми библиотеками и возможностями JQuery и т. д.

Я бы не рискнул разрабатывать такое приложение на APEX, на мой взгляд, требования слишком далеки от технологии, предлагаемой в APEX.

с другой стороны вы говорите о производительности БД.. Хорошо, что вы принимаете это во внимание на этапе планирования, но плохо, потому что вы ограничиваете уже себя.. есть варианты... например, получить опцию в памяти в базе данных и / кэшировать результаты из материализованного представления.. Закрепите таблицы в памяти.