SQL - сумма данных за все время, 30 дней и 90 дней для нескольких столбцов индивидуально - PullRequest
4 голосов
/ 18 февраля 2020

ФОН:

У меня есть данные, которые выглядят следующим образом

date        src    subsrc   subsubsrc   param1  param2
2020-02-01  src1    ksjd    dfd8        47      31    
2020-02-02  src1    djsk    zmnc        44      95    
2020-02-03  src2    skdj    awes        92      100   
2020-02-04  src2    mxsf    kajs        80      2     
2020-02-05  src3    skdj    asio        46      53    
2020-02-06  src3    dekl    jdqo        19      18    
2020-02-07  src3    dskl    dqqq        69      18    
2020-02-08  src4    sqip    riow        64      46    
2020-02-09  src5    ss01    qwep        34      34    

Я пытаюсь агрегировать за все время, последние 30 дней и последние 90 дней ( без скользящей суммы)

Таким образом, мои окончательные данные будут выглядеть так:

src     subsrc  subsubsrc   p1_all  p1_30   p1_90   p2_all  p2_30   p2_90
src1    ksjd    dfd8        7       1       7       98      7        98
src1    djsk    zmnc        0       0       0       0       0         0
src2    skdj    awes        12      12      12      4       4         4
src2    mxsf    kajs        6       6       6       31      31       31
src3    skdj    asio        0       0       0       0       0         0
src3    dekl    jdqo        20      20      20      17      17        17
src3    dskl    dqqq        3       3       3       4       4         4
src4    sqip    qwep        0       0       0       0       0         0
src5    ss01    qwes        15      15      15      2       2         2

О ДАННЫХ:

  • Это только фиктивная данные и, следовательно, неверны.
  • В моих данных десятки тысяч строк.
  • Существует дюжина столбцов sr c, составляющих ключ для таблицы.
  • Есть дюжина столбцов параметров, которые я должен суммировать для 30 и 90 и все время.
  • Также в столбцах параметров есть нулевые значения.
  • Также может быть несколько строки для того же дня и столбца sr c.
  • Новые данные добавляются каждый день, и, вероятно, запрос будет выполняться каждый день, чтобы получить последние 30, 90 данных за все время.

ЧТО Я ПОПЫТАЛ:

Вот что я придумал:

SELECT src, subsubsrc, subsubsrc,
SUM(param1) as param1_all,
SUM(CASE WHEN DATE_DIFF(CURRENT_DATE,date,day) <= 30 THEN param1 END) as param1_30,
SUM(CASE WHEN DATE_DIFF(CURRENT_DATE,date,day) <= 90 THEN param1 END) as param1_90,
SUM(param2) as param2_all,
SUM(CASE WHEN DATE_DIFF(CURRENT_DATE,date,day) <= 30 THEN param2 END) as param2_30,
SUM(CASE WHEN DATE_DIFF(CURRENT_DATE,date,day) <= 90 THEN param2 END) as param2_90,
FROM `MY_TABLE`
GROUP BY src
ORDER BY src

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

Я пробовал что-то, что называется " Фильтрованные агрегатные функции (или ручное управление) " объяснил ЗДЕСЬ . Но я не могу понять / реализовать это для моего случая.

Также я просмотрел десятки ответов, и большинство из них используют суммы за каждый день ИЛИ сложные случаи этого базового расчета c. Возможно, я не ищу его правильно.

Как видите, я новичок ie в SQL и буду очень признателен за любую помощь.

Ответы [ 4 ]

1 голос
/ 19 февраля 2020

Ваш запрос выглядит неплохо; условная агрегация - это канонический метод для поворота набора данных.

Одним из способов повышения производительности может быть изменение фильтра даты в условных выражениях: использование функции даты исключает использование индекса.

Вместо этого вы можете сформулировать это следующим образом:

select 
    src, 
    subsrc, 
    subsubsrc,
    sum(param1) as param1_all,
    sum(case when date >= current_date - interval 30 day then param1 end) as param1_30,
    sum(case when date >= current_date - interval 90 day then param1 end) as param1_90,
    sum(param2) as param2_all,
    sum(case when date >= current_date - interval 30 day then param2 end) as param2_30,
    sum(case when date >= current_date - interval 90 day then param2 end) as param2_90
from my_table
group by src, subsrc, subsubsrc
order by src, subsrc, subsubsrc

Для производительности может помочь следующий индекс: (src, subsrc, subsubsrc, date).

Обратите внимание, что я включил все три неагрегированных столбца ( src, subsrc, subsubsrc) в предложении group by: начиная с MySQL 5.7, это является обязательным по умолчанию (хотя вы можете поиграть с режимами sql, чтобы изменить это поведение) - и большинство других баз данных реализуют то же ограничение.

0 голосов
/ 18 февраля 2020

Ваш первый подход не плох, если вы можете построить запрос программно. В качестве альтернативы можно сначала создать дополнительные таблицы для случаев 30 и 90 дней, чтобы можно было эффективно выбрать все столбцы из каждого. Это также может быть сделано в подзапросах, но есть соображения производительности.

Некоторый непроверенный псевдокод, который мы надеемся уточнить:

SELECT 
 src,
 subsrc,
 subsubsrc,
 SUM(param1) as param1_all,
 -- other "all" sums here
 SUM(t30.param1) as param1_30,
 -- other "30" sums here
 SUM(t90.param1) as param1_90,
 -- other "90" sums here
FROM MY_TABLE
LEFT JOIN (
  SELECT *
  FROM MY_TABLE
  WHERE date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
) as t30 on t30.src = MY_TABLE.src
LEFT JOIN (
  SELECT *
  FROM MY_TABLE
  WHERE date >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)
) as t90 on t90.src = MY_TABLE.src
GROUP BY MY_TABLE.src
ORDER BY MY_TABLE.src

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

Если вы сначала поместите эти подзапросы в дополнительные таблицы с ключом на sr c, объединения также будут более эффективными. Вы даже можете сначала сгруппировать / суммировать непосредственно в эти боковые таблицы, а не создавать большие копии своих данных, а затем объединить агрегированные данные вместе.

0 голосов
/ 19 февраля 2020

Ваш код выглядит хорошо. Ваша СУБД должна * l oop все записи под капотом и сделать некоторые вычисления. Одна вещь, которую вы можете улучшить, это то, что вы рассчитываете разницу во времени для всех записей. Имеет смысл заранее рассчитать момент 30 дней go и 90 дней go соответственно и сравнить только даты с этими.

Поскольку вы уже знаете, что число строк и параметров будет в будущем, имеет смысл создать задание cron, которое ежедневно вычисляет это следующим образом:

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

Если вы сделаете это правильно и получите ежедневную информацию, то позже вы сможете анализировать тенденции в истории.

0 голосов
/ 18 февраля 2020

Я бы порекомендовал использовать для этого 3 разных запроса:

  1. Сумма за все время
  2. Сумма за 30 дней
  3. Сумма за 90 дней

Потому что, когда вы пытаетесь выполнить запрос all-in-1, вы в итоге получаете полное сканирование таблицы из-за CASE-WHEN-END (КСТАТИ есть компактная форма IF() в MySQL). Это крайне неоптимально.

Если вы разделите его на 3 разных запроса и добавите индекс в столбец date, тогда для 2-го и 3-го запроса он не будет выполнять полное сканирование. Только для 1-го запроса, который можно оптимизировать отдельно (например, путем кэширования).

Также этот подход: DATE_DIFF(CURRENT_DATE,date,day) <= 90

следует изменить на: date >= 'date-90-days-ago' (где 'date-90-days-ago' является фиксированной датой)

Таким образом, вам не нужно будет вычислять разницу в 2 даты для каждой строки. Вам нужно будет просто вычислить 2 даты: 30 дней go и 90 дней go и сравнить все остальные даты с этими двумя. Этот подход будет использовать индекс столбца date.

...