Розробка зведених таблиць (Pivot Table) для аналітики на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка зведених таблиць (Pivot Table) для аналітики на сайті
Складна
~5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка зведених таблиць (Pivot Table) для аналітики

Зведена таблиця—це UI для групування, агрегації та порівняння даних у реальному часі. Користувачі перетягують поля між осями, вибирають метрики та отримують потрібні зрізи без звертання до розробника. Excel та Google Sheets популярні саме завдяки цій можливості—вбудовування аналогічного інструменту в веб-застосунок усуває потребу експортувати дані.

Архітектура

Два принципово різних підходи:

Клієнтська агрегація—всі дані завантажені в браузер, pivot розраховується у JavaScript. Працює до ~100–200k рядків. Швидкий відклик при маніпуляціях з осями, не потребує round-trip до сервера.

Серверна агрегація—кожна зміна конфігурації відправляє запит на сервер, база даних розраховує агрегати. Обов'язково для великих обсягів. ClickHouse або PostgreSQL з правильними індексами повертають агрегати з мільйонів рядків за сотні мілісекунд.

Готові бібліотеки

Перед написанням з нуля, оцініть:

  • react-pivottable—відкритий код, drag-and-drop, кілька рендерерів (таблиця, bar chart, heatmap). Обмежена клієнтською обробкою
  • AG Grid (з row grouping)—enterprise-рівень, серверний режим, величезна екосистема. Платна для передових функцій
  • Flexmonster—спеціалізований pivot, підключається до OLAP-кубів, комерційна ліцензія

Клієнтська реалізація

Ядро pivot—функція агрегації:

type AggregateFunction = 'sum' | 'count' | 'avg' | 'min' | 'max';

interface PivotConfig {
  rows: string[];      // поля для рядків
  cols: string[];      // поля для стовпців
  values: string[];    // числові поля
  aggFn: AggregateFunction;
  filters: Record<string, string[]>;  // поле -> дозволені значення
}

interface PivotResult {
  rowKeys: string[][];
  colKeys: string[][];
  data: Map<string, Map<string, number>>;
}

function computePivot(rawData: Record<string, any>[], config: PivotConfig): PivotResult {
  const { rows, cols, values, aggFn, filters } = config;

  // Застосуйте фільтри
  const filtered = rawData.filter(row =>
    Object.entries(filters).every(([field, allowed]) =>
      !allowed.length || allowed.includes(String(row[field]))
    )
  );

  // Зберіть унікальні ключі рядків та стовпців
  const rowKeySet = new Set<string>();
  const colKeySet = new Set<string>();
  const accumulator = new Map<string, Map<string, number[]>>();

  filtered.forEach(row => {
    const rowKey = rows.map(r => String(row[r] ?? '(порожньо)')).join('||');
    const colKey = cols.map(c => String(row[c] ?? '(порожньо)')).join('||');

    rowKeySet.add(rowKey);
    colKeySet.add(colKey);

    const numVal = values.reduce((sum, v) => sum + (Number(row[v]) || 0), 0);

    if (!accumulator.has(rowKey)) accumulator.set(rowKey, new Map());
    const colMap = accumulator.get(rowKey)!;
    if (!colMap.has(colKey)) colMap.set(colKey, []);
    colMap.get(colKey)!.push(numVal);
  });

  // Агрегуйте
  const aggregated = new Map<string, Map<string, number>>();
  accumulator.forEach((colMap, rowKey) => {
    const row = new Map<string, number>();
    colMap.forEach((vals, colKey) => {
      let result: number;
      switch (aggFn) {
        case 'sum':   result = vals.reduce((a, b) => a + b, 0); break;
        case 'count': result = vals.length; break;
        case 'avg':   result = vals.reduce((a, b) => a + b, 0) / vals.length; break;
        case 'min':   result = Math.min(...vals); break;
        case 'max':   result = Math.max(...vals); break;
      }
      row.set(colKey, result);
    });
    aggregated.set(rowKey, row);
  });

  return {
    rowKeys: Array.from(rowKeySet).sort().map(k => k.split('||')),
    colKeys: Array.from(colKeySet).sort().map(k => k.split('||')),
    data: aggregated,
  };
}

Компонент таблиці

Користувальницький UI pivot рендерить ефективно з віртуалізованими рядками:

Ключові функції:

  • Drag-and-drop конфігурація осі
  • Агрегація в реальному часі
  • Проміжні підсумки та загальний підсумок
  • Експорт у CSV/Excel
  • Умовне форматування для значень

Серверна реалізація

Для мільйонів рядків використовуйте вікнові функції:

SELECT
  date_trunc('month', created_at)::date AS month,
  category,
  SUM(amount) AS total,
  COUNT(*) AS cnt,
  AVG(amount) AS avg_amount
FROM orders
WHERE created_at > NOW() - INTERVAL '12 months'
GROUP BY 1, 2
ORDER BY 1, 2;

Індексування на (category, created_at) або композитні індекси різко прискорюють агрегацію.

Часова шкала

Базова клієнтська pivot з drag-drop UI—3–5 днів. Серверна реалізація з фільтруванням, drill-down та експортом—7–10 днів.