SQL Windows Функция - заполнение строк между диапазонами дат для визуализации движения статуса - PullRequest
0 голосов
/ 24 января 2020

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

Конечная цель состоит в том, чтобы каждый день отображать статус каждого клиента, чтобы показать его «движение» через время. Позже я хотел бы показать в Таблице статус каждого дня, какой статус имеет клиент. Проблема заключается в том, что исходные данные не хранятся таким образом, который был бы полезен для меня, и поэтому я прибегнул к переносу данных в первую очередь в таблицу, которая показывает мне для каждого клиента запись, касающуюся каждого изменения статуса, добавляя «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, вместе взятый, я уверен, что есть что-то более скудное. Что касается последней части заполнения строк, я не знаю, как к ней подойти, и если это даже разумный подход, то, что я сделал.

Любое руководство будет отличным!

Спасибо!

...