В базе данных транзакций, охватывающей тысячи субъектов в течение 18 месяцев, я хотел бы выполнить запрос для группировки каждого возможного 30-дневного периода по entity_id
сумме их сумм транзакций и количеству их транзакций за этот 30-дневный период, и вернуть данные таким образом, что я могу затем запросить. После большого тестирования этот код выполняет большую часть того, что я хочу:
SELECT id, trans_ref_no, amount, trans_date, entity_id,
SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
COUNT(id) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
FROM transactiondb;
И я буду использовать в более крупном запросе, структурированном что-то вроде:
SELECT * FROM (
SELECT id, trans_ref_no, amount, trans_date, entity_id,
SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
COUNT(id) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
FROM transactiondb ) q
WHERE trans_count >= 4
AND trans_total >= 50000;
Случай, который не покрывается этим запросом, - это когда количество транзакций будет составлять несколько месяцев, но все равно будет в течение 30 дней друг от друга. Возможен ли этот тип запроса с Postgres? Если это так, я приветствую любой вклад. Многие другие темы обсуждают " бегущие " агрегаты, а не прокатку .
Обновить
CREATE TABLE
Скрипт:
CREATE TABLE transactiondb (
id integer NOT NULL,
trans_ref_no character varying(255),
amount numeric(18,2),
trans_date date,
entity_id integer
);
Пример данных можно найти здесь . Я использую PostgreSQL 9.1.16.
Идеальный результат будет включать SUM(amount)
и COUNT()
всех транзакций в течение 30-дневного периода. Посмотрите это изображение, например:
Зеленая подсветка даты указывает на то, что включено в мой запрос. Желтая подсветка строки указывает на записи, что я хотел бы стать частью набора.
Предыдущее чтение:
источник
every possible 30-day period by entity_id
вам означает , что период может начать любой день, так что 365 возможных периодов в (не високосный) год? Или вы хотите рассматривать дни с фактической транзакцией как начало периода отдельно для любогоentity_id
? В любом случае, пожалуйста, укажите определение таблицы, версию Postgres, некоторые примеры данных и ожидаемый результат для образца.entity_id
в 30-дневном окне, начиная с каждой фактической транзакции. Может ли быть несколько транзакций для одного(trans_date, entity_id)
и того же или эта комбинация определена уникально? В определении вашей таблицы нетUNIQUE
ограничения или ограничения PK, но ограничения, по-видимому, отсутствуют ...id
первичный ключ. Может быть несколько транзакций на объект в день.Ответы:
Запрос у вас есть
Вы можете упростить запрос, используя
WINDOW
предложение, но это только сокращает синтаксис, а не изменяет план запроса.count(*)
, так какid
это определеноNOT NULL
?ORDER BY entity_id
так как вы ужеPARTITION BY entity_id
Вы можете упростить и дальше:
не добавляйте
ORDER BY
в определение окна вообще, это не относится к вашему запросу. Тогда вам не нужно определять пользовательскую рамку окна, либо:Проще, быстрее, но все же просто лучшая версия того, что у вас есть , со статичными месяцами.
Запрос, который вы можете захотеть
... не четко определено, поэтому я буду опираться на эти предположения:
Подсчитывать транзакции и суммы за каждый 30-дневный период в пределах первой и последней транзакции любого
entity_id
. Исключите начальные и конечные периоды без активности, но включите все возможные 30-дневные периоды в пределах этих внешних границ.Здесь перечислены все 30-дневные периоды для каждого
entity_id
с вашими агрегатами и сtrans_date
первым днем (вкл.) Периода. Чтобы получить значения для каждой отдельной строки, присоединитесь к базовой таблице еще раз ...Основная сложность та же, что и здесь:
Определение фрейма окна не может зависеть от значений текущей строки.
А точнее позвоните
generate_series()
сtimestamp
вводом:Запрос, который вы на самом деле хотите
После обновления вопроса и обсуждения:
накапливайте одинаковые строки
entity_id
в 30-дневном окне, начиная с каждой фактической транзакции.Поскольку ваши данные распределены редко, более эффективным будет запуск самосоединения с условием диапазона , тем более что Postgres 9.1 пока не имеет
LATERAL
объединений:SQL Fiddle.
Скользящее окно может иметь смысл (в отношении производительности) только для данных в течение большинства дней.
Это не объединяет дубликаты
(trans_date, entity_id)
за день, но все строки одного и того же дня всегда включаются в 30-дневное окно.Для большой таблицы такой индекс покрытия может очень помочь:
Последний столбец
amount
полезен только в том случае, если вы получаете только сканирование по индексу. Остальное брось.Но он все равно не будет использоваться, пока вы выбираете всю таблицу. Он будет поддерживать запросы для небольшого подмножества.
источник
column "t0.amount" must appear in the GROUP BY clause...