提供的資料用于經常性收入(每月),列定義如下:
- 客戶 ID - 帳戶欄位(非唯一)
- 合同開始日期 - Opp 開始日期
- 合同結束日期 – OPP 合同結束日期
- 合同價值 – MRR(每月預期收入)
| 開始日期 | OPP 合同結束日期 | 帳戶欄位 | MRR(每月預期收入) |
|---|---|---|---|
| 1/2/2013 | 1/2/2015 | 50e55 | 195.00 |
| 1/2/2013 | 1/2/2014 | 4ee75 | 50.00 |
| 1/2/2013 | 1/2/2014 | 4f031 | 75.00 |
| 1/2/2013 | 1/2/2016 | 4c3b2 | 133.00 |
| 1/2/2013 | 1/2/2016 | 49ec8 | 132.00 |
| 1/3/2013 | 1/3/2014 | 49fc8 | 59.00 |
| 1/4/2013 | 1/4/2015 | 49wc8 | 87.00 |
| 12/27/2013 | 12/27/2014 | 50bf7 | 190.00 |
| 12/27/2013 | 12/27/2014 | 59bb8 | 179.00 |
| 12/27/2013 | 12/27/2014 | 49ec8 | 147.00 |
具體來說,在滾動的 12 個月期間,我想將每個月的收入分配到以下類別:
- 追加銷售——現有客戶的總收入比 12 個月前增加(包括現有客戶的新合同/到期合同)
- 降價銷售——現有客戶的總收入比 12 個月前減少(包括現有客戶的新合同/到期合同)
- 流失——客戶離開帶來的收入減少(即他們 12 個月前是客戶,但不再是客戶;即他們的最后一份合同已到期)
- 新客戶 – 來自新客戶的收入增加(即 12 個月前不是客戶的客戶;基于客戶的第一份新合同。)
如何在 SQL 中實作這一點?
鑒于上述資料,預期結果(建議):
--------- --------- -------- ---------- --------- --------
| Month | MRR | upsell | downsell | newsell | churn |
--------- --------- -------- ---------- --------- --------
| 2013-01 | 731.00 | NULL | NULL | 731.00 | NULL | (195 50 75 133 132 59 87)
| 2013-02 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-03 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-04 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-05 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-06 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-07 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-08 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-09 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-10 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-11 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-12 | 1247.00 | NULL | NULL | 1247.00 | NULL | (731 190 179 147)
| 2014-01 | 1247.00 | 147.00 | NULL | 369.00 | NULL | (Account='49ec8' added 147. The rest are new Account contracts. Year over year this month)
| 2014-02 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-03 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-04 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-05 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-06 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-07 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-08 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-09 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-10 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-11 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-12 | 1063.00 | NULL | NULL | NULL | 184.00 |
| 2015-01 | 547.00 | NULL | -147.00 | NULL | 553.00 |
| 2015-02 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-03 | 265.00 | NULL | -147.00 | NULL | 651.00 |
... etc.
注意:向上銷售和向下銷售的情況要求賬戶能夠擁有多個關聯的合約(例如:賬戶 = '49ec8')。
測驗用例:
CREATE TABLE contracts (
StartDate DATE
, EndDate DATE
, Account VARCHAR(20)
, MRR DECIMAL(8,2)
);
INSERT INTO contracts VALUES
('1/2/2013' , '1/2/2015' , '50e55', 195.00)
, ('1/2/2013' , '1/2/2014' , '4ee75', 50.00)
, ('1/2/2013' , '1/2/2014' , '4f031', 75.00)
, ('1/2/2013' , '1/2/2016' , '4c3b2', 133.00)
, ('1/2/2013' , '1/2/2016' , '49ec8', 132.00)
, ('1/3/2013' , '1/3/2014' , '49fc8', 59.00)
, ('1/4/2013' , '1/4/2015' , '49wc8', 87.00)
, ('12/27/2013' , '12/27/2014' , '50bf7', 190.00)
, ('12/27/2013' , '12/27/2014' , '49cc8', 179.00)
, ('12/27/2013' , '12/27/2014' , '49ec8', 147.00)
;
uj5u.com熱心網友回復:
這是我的嘗試,它生成了問題中顯示的部分結果。有多種方法可以做到這一點。我選擇主要使用視窗函式而不是外部連接來執行此操作。哪個都可以用。
小提琴
| CTE術語 | 描述 |
|---|---|
| 擴張 | 遞回生成每個合約的月收入行 |
| 步驟0 | 總結每個賬戶每月的收入 |
| 步驟1 | 查找每個帳戶上一年(月)的收入 |
| 步驟1 | ... 還可以找到每個帳戶的下一年(月)收入,用于添加代表沒有進一步合同的帳戶的行 |
| 第2步 | 將去年 MRR 詳細資訊與代表沒有進一步合同的帳戶的行相結合 |
| 第三步 | 與上述:計算向上銷售,向下銷售,新聞銷售,流失 |
| 最終表達 | SUM 并每月生成結果 |
WITH expand (StartDate, EndDate, Account, MRR, CurrDate) AS (
SELECT c.*, CAST(CONCAT(LEFT(StartDate,7), '-01') AS date) FROM contracts AS c UNION ALL
SELECT StartDate, EndDate, Account, MRR, DATEADD(month, 1, CurrDate) FROM expand
WHERE CurrDate < EndDate
)
, step0 AS (
SELECT Account, SUM(MRR) AS MRR, CurrDate, COUNT(*) AS n
FROM expand
GROUP BY Account, CurrDate
)
, step1 AS (
SELECT *
, LAG( MRR, 12) OVER (PARTITION BY Account ORDER BY CurrDate) AS LYMRR
, LEAD(MRR, 12) OVER (PARTITION BY Account ORDER BY CurrDate) AS MRR2
FROM step0
)
, step2 AS (
SELECT Account, MRR, CurrDate, n, LYMRR FROM step1 UNION ALL
SELECT Account, MRR2 AS MRR, DATEADD(year, 1, CurrDate) AS CurrDate, 0 AS n, MRR AS LYMRR
FROM step1
WHERE MRR2 IS NULL
)
, step3 AS (
SELECT Account, MRR, CurrDate, n, LYMRR
, CASE WHEN MRR > LYMRR THEN MRR - LYMRR END AS upsell
, CASE WHEN MRR < LYMRR THEN MRR - LYMRR END AS downsell
, CASE WHEN LYMRR IS NULL THEN MRR END AS newsell
, CASE WHEN MRR IS NULL THEN LYMRR END AS churn
FROM step2
)
SELECT LEFT(CurrDate, 7) AS Month
, SUM(MRR) AS MRR
, SUM(upsell) AS upsell
, SUM(downsell) AS downsell
, SUM(newsell) AS newsell
, SUM(churn) AS churn
FROM step3
GROUP BY CurrDate
ORDER BY CurrDate
;
結果:
--------- --------- -------- ---------- --------- --------
| Month | MRR | upsell | downsell | newsell | churn |
--------- --------- -------- ---------- --------- --------
| 2013-01 | 731.00 | NULL | NULL | 731.00 | NULL | (195 50 75 133 132 59 87)
| 2013-02 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-03 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-04 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-05 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-06 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-07 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-08 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-09 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-10 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-11 | 731.00 | NULL | NULL | 731.00 | NULL |
| 2013-12 | 1247.00 | NULL | NULL | 1247.00 | NULL | (731 190 179 147)
| 2014-01 | 1247.00 | 147.00 | NULL | 369.00 | NULL | (Account='49ec8' added 147. The rest are new Account contracts. Year over year this month)
| 2014-02 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 | (churn = 50 75 59 accounts with no further contracts)
| 2014-03 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-04 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-05 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-06 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-07 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-08 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-09 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-10 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-11 | 1063.00 | 147.00 | NULL | 369.00 | 184.00 |
| 2014-12 | 1063.00 | NULL | NULL | NULL | 184.00 |
| 2015-01 | 547.00 | NULL | -147.00 | NULL | 553.00 |
| 2015-02 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-03 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-04 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-05 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-06 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-07 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-08 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-09 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-10 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-11 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2015-12 | 265.00 | NULL | -147.00 | NULL | 651.00 |
| 2016-01 | 265.00 | NULL | NULL | NULL | 282.00 |
| 2016-02 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-03 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-04 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-05 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-06 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-07 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-08 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-09 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-10 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-11 | NULL | NULL | NULL | NULL | 265.00 |
| 2016-12 | NULL | NULL | NULL | NULL | 265.00 |
| 2017-01 | NULL | NULL | NULL | NULL | 265.00 |
--------- --------- -------- ---------- --------- --------
設定:
CREATE TABLE contracts (
StartDate DATE
, EndDate DATE
, Account VARCHAR(20)
, MRR DECIMAL(8,2)
);
INSERT INTO contracts VALUES
('1/2/2013' , '1/2/2015' , '50e55', 195.00)
, ('1/2/2013' , '1/2/2014' , '4ee75', 50.00)
, ('1/2/2013' , '1/2/2014' , '4f031', 75.00)
, ('1/2/2013' , '1/2/2016' , '4c3b2', 133.00)
, ('1/2/2013' , '1/2/2016' , '49ec8', 132.00)
, ('1/3/2013' , '1/3/2014' , '49fc8', 59.00)
, ('1/4/2013' , '1/4/2015' , '49wc8', 87.00)
, ('12/27/2013' , '12/27/2014' , '50bf7', 190.00)
, ('12/27/2013' , '12/27/2014' , '49cc8', 179.00)
, ('12/27/2013' , '12/27/2014' , '49ec8', 147.00)
;
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/385365.html
標籤:sql sql-server
上一篇:如何在mysql中使用最后一個值視窗分析函式來結轉時間序列中缺失日期的值
下一篇:SQL中的求和非空值塊
