Буду признателен за вашу компетентность в решении проблемы, с которой я столкнулся при хранении данных, которые я хранил.
Конечная цель состоит в том, чтобы каждый день отображать статус каждого клиента, чтобы показать его «движение» через время. Позже я хотел бы показать в Таблице статус каждого дня, какой статус имеет клиент. Проблема заключается в том, что исходные данные не хранятся таким образом, который был бы полезен для меня, и поэтому я прибегнул к переносу данных в первую очередь в таблицу, которая показывает мне для каждого клиента запись, касающуюся каждого изменения статуса, добавляя «start_date» и 'end_date (см. промежуточные данные).
Чего мне не хватает сейчас, я считаю, так это создания строк «заполнителя» для дней между значениями «start_date» и «end_date» каждого статуса, чтобы иметь запись для каждого дня. Я полагаю, что это способ, который позволит мне показать движение, которое я ищу.
Я не могу понять, как это сделать, я использовал функцию 'generate_series' из PostgreSQL, которую я оставил присоединиться. Результаты не то, что я искал (результаты показаны в последней таблице).
Я попытался изобразить ниже данных и SQL, которые я использую для достижения оптимального результата.
Начальная точка (таблица customer_statuses):
customer_id | status | created_at
---------------------------------------------------
abcdefg1234 D 2019-08-22
abcdefg1234 C 2019-01-17
abcdefg1234 A 2018-01-18
Промежуточный результат (Текущая точка):
customer_id | status | start_date | end_date
---------------------------------------------------
abcdefg1234 D 2019-08-22
abcdefg1234 C 2019-01-17 2019-08-22
abcdefg1234 A 2018-01-18 2019-01-17
SQL:
SELECT
a.customer_id,
a.status,
CASE WHEN a.seqnum = LAST_VALUE(a.seqnum) OVER (PARTITION BY a.customer_id) THEN a.created_at::DATE
ELSE a.created_at::DATE
END AS start_date,
CASE WHEN seqnum - 1 = LAG(seqnum) OVER (ORDER BY a.created_at DESC)
THEN LAG(a.created_at::DATE) OVER (ORDER BY a.created_at DESC)
END AS end_date
FROM
(
SELECT DISTINCT cs1.customer1,
cs1.created_at,
cs1.status,
ROW_NUMBER() over (PARTITION BY cs1.customer_id ORDER BY cs1.created_at DESC) AS seqnum
FROM customer_statuses cs1
INNER JOIN customer_statuses cs2
ON cs1.customer_id = cs2.customer_id
AND cs1.created_at = cs1.created_at
WHERE cs1.customer1='abcdefg1234'
ORDER BY 2 DESC
) a
SQL - это в черновом режиме:
WITH dates AS (
SELECT
generate_series(
MIN(DATE_TRUNC('day', '2018-04-18'::DATE)::DATE),
MAX(DATE_TRUNC('day', '2019-07-31'::date)::DATE), '1d'
)::DATE AS day
)
SELECT
d.day,
bus.customer_id,
bus.status,
bus.start_date,
bus.end_date,
last_value(bus.status) OVER (PARTITION BY bus.customer_id) AS corrected_status
FROM dates d
LEFT OUTER JOIN
(
SELECT
a.customer_id,
a.status,
CASE WHEN a.seqnum = LAST_VALUE(a.seqnum) OVER (PARTITION BY a.customer_id) THEN a.created_at::DATE
ELSE a.created_at::DATE
END AS start_date,
CASE WHEN seqnum - 1 = LAG(seqnum) OVER (ORDER BY a.created_at DESC)
THEN LAG(a.created_at::DATE) OVER (ORDER BY a.created_at DESC)
END AS end_date
FROM
(
SELECT DISTINCT cs1.customer1,
cs1.created_at,
cs1.status,
ROW_NUMBER() over (PARTITION BY cs1.customer_id ORDER BY cs1.created_at DESC) AS seqnum
FROM customer_statuses cs1
INNER JOIN customer_statuses cs2
ON cs1.customer_id = cs2.customer_id
AND cs1.created_at = cs1.created_at
WHERE cs1.customer1='abcdefg1234'
ORDER BY 2 DESC
) a
) as bus
ON d.day::date >= bus.start_date --and d.day < bus.end_date
Требуемый результат:
day | customer_id | status |
----------------------------------
2020-01-23 abcdefg1234 D
...
2019-08-23 abcdefg1234 D
2019-08-22 abcdefg1234 D
...
2019-01-18 abcdefg1234 C
2019-01-17 abcdefg1234 C
...
2018-01-19 abcdefg1234 A
2018-01-18 abcdefg1234 A
Сам по себе промежуточный шаг довольно грязный SQL, вместе взятый, я уверен, что есть что-то более скудное. Что касается последней части заполнения строк, я не знаю, как к ней подойти, и если это даже разумный подход, то, что я сделал.
Любое руководство будет отличным!
Спасибо!