簡體   English   中英

SQL 查找過去 30 天記錄計數按分組

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM