[英]SQL Find Last 30 Days records count grouped by
我正在嘗試在動態窗口中檢索每個狀態每天的客戶數 - 過去 30 天。 查詢結果應顯示過去 30 天(即今天 () - 29 天)內每個客戶狀態(A、B、C)每天有多少客戶。 每個客戶一次可以有一種狀態,但在客戶生命周期內從一種狀態變為另一種狀態。 此查詢的目的是顯示客戶在其整個生命周期中的“移動”。 我生成了一系列日期,從創建客戶的第一個日期到今天。
我已將以下查詢放在一起,但似乎我正在做的事情是不正確的,因為結果將大多數日子描述為在所有狀態中具有相同的計數,這是不可能的,每天都會創建新客戶。 我們檢查了另一個簡單的查詢並確認狀態之間的分割不相等。
我試圖在數據和我用來達到最佳結果的 SQL 下方進行描述。
起點(示例表 customer_statuses):
customer_id | status | created_at
---------------------------------------------------
abcdefg1234 B 2019-08-22
abcdefg1234 C 2019-01-17
...
abcdefg1234 A 2018-01-18
bcdefgh2232 A 2017-09-02
ghijklm4950 B 2018-06-06
狀態 - A、B、C 狀態沒有先后順序,客戶可以在業務關系開始時擁有任何狀態,並在其整個生命周期內在狀態之間切換。
餐桌客戶:
id | f_name | country | created_at |
---------------------------------------------------------------------
abcdefg1234 Michael FR 2018-01-18
bcdefgh2232 Sandy DE 2017-09-02
....
ghijklm4950 Daniel NL 2018-06-06
SQL - 當前版本:
WITH customer_list AS (
SELECT
DISTINCT a.id,
a.created_at
FROM
customers a
),
dates AS (
SELECT
generate_series(
MIN(DATE_TRUNC('day', created_at)::DATE),
MAX(DATE_TRUNC('day', now())::DATE),
'1d'
)::date AS day
FROM customers a
),
customer_statuses AS (
SELECT
customer_id,
status,
created_at,
ROW_NUMBER() OVER
(
PARTITION BY customer_id
ORDER BY created_at DESC
) col
FROM
customer_status
)
SELECT
day,
(
SELECT
COUNT(DISTINCT id) AS accounts
FROM customers
WHERE created_at::date BETWEEN day - 29 AND day
),
status
FROM dates d
LEFT JOIN customer_list cus
ON d.day = cus.created_at
LEFT JOIN customer_statuses cs
ON cus.id = cs.customer_id
WHERE
cs.col = 1
GROUP BY 1,3
ORDER BY 1 DESC,3 ASC
目前查詢結果如下:
day | count | status
-------------------------
2020-01-24 1230 C
2020-01-24 1230 B
2020-01-24 1230 A
2020-01-23 1200 C
2020-01-23 1200 B
2020-02-23 1200 A
2020-02-22 1150 C
2020-02-22 1150 B
...
2017-01-01 50 C
2017-01-01 50 B
2017-01-01 50 A
我從上面的結果中注意到了兩件事 - 大多數情況下,結果在給定日期的所有狀態中顯示相同的計數。 第二個觀察結果,有些日子只出現兩種狀態 - 這不應該是這種情況。 如果現在在特定日期創建具有特定狀態的新帳戶,則應結轉前一天的計數 - 對嗎? 或者這是我創建的查詢或我想到的邏輯的問題? 也許我期待的結果在邏輯上不會發生?
要求的結果:
day | count | status
-------------------------
2020-01-24 1230 C
2020-01-24 1000 B
2020-01-24 2500 A
2020-01-23 1200 C
2020-01-23 1050 B
2020-02-23 2450 A
2020-02-22 1160 C
2020-02-22 1020 B
2020-02-22 2400 A
...
2017-01-01 10 C
2017-01-01 4 B
2017-01-01 50 A
謝謝你!
您的查詢似乎過於復雜。 這是另一種方法:
lead()
獲取每個客戶狀態記錄的狀態何時結束。generate_series()
生成日期。剩下的只是過濾和聚合:
select gs.dte, cs.status, count(*)
from (select cs.*,
lead(cs.created_at, 1, now()::date) over (partition by cs.customer_id order by cs.created_at) as next_ca
from customer_statuses cs
) cs cross join lateral
generate_series(cs.created_at, cs.next_ca - interval '1 day', interval '1 day') gs(dte)
where gs.dte < now()::date - interval '30 day'
我稍微更改了查詢,因為我注意到我在客戶更改狀態的那幾天收到了重復的記錄 - 一條記錄具有舊狀態,另一條記錄為新一天。
例如帶有@Gordon 查詢的輸出:
dte | status
---------------------------
2020-02-12 B
... ...
01.02.2020 A
01.02.2020 B
31.01.2020 A
30.01.2020 A
我已經調整了查詢,見下文,雖然結果正確地描述了狀態之間的變化(在變化當天沒有重復的記錄),但是,記錄一直持續到now()::date - interval '1day'
而不是包括now()::date
(如今天)。 我不確定為什么,也找不到正確的邏輯來確保所有這一切都是我想要的。 日期正確地描述了每個客戶的狀態,返回的狀態包括今天。
調整后的查詢:
select gs.dte, cs.status, count(*)
from (select cs.*,
lead(cs.created_at, 1, now()::date) over (partition by cs.customer_id order by cs.created_at) - INTERVAL '1day' as next_ca
from customer_statuses cs
) cs cross join lateral
generate_series(cs.created_at, cs.next_ca, interval '1 day') gs(dte)
where gs.dte < now()::date - interval '30 day'
兩個調整:調整似乎也違反直覺,因為我似乎將間隔日從查詢的一部分中取出,只是為了將其添加到另一部分(對我來說似乎產生相同的結果)
a - 增加了從領先功能(第 3 行)減少 1 天的時間
lead(cs.created_at, 1, now()::date) over (partition by cs.customer_id order by cs.created_at) - INTERVAL '1 day' as next_ca
b - 從 next_ca 變量中刪除 1 天的減少(第 6 行)
generate_series(cs.created_at, cs.next_ca - interval '1 day', interval '1 day')
調整后查詢的輸出示例:
dte | status
---------------------------
2020-02-11 B
... ...
01.02.2020 B
31.01.2020 A
30.01.2020 A
謝謝你的幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.