前幾天,有一個業務,這個業務是統計每個月的累計用戶數量,用於做大屏展示使用。
爲了省去前端的開發時間,大屏展示使用了開源工具davinci,該工具集成了echarts報表,可以通過編寫SQL,選擇圖表類型,拖動和縮放圖表生成自己的報表或大屏頁面。
但是這樣就導致了不能在查詢出結果後,不能對數據進行二次加工;另外我們本次連的是業務庫,我們只有查詢權限,這就封禁了建中間表的方式。所以只能在SQL上下功夫了。
以下是我所有sql的迭代過程。
1、數據庫簡化模型
t_user用戶表簡化模型如下:
user_name | user_type | create_month |
---|---|---|
張一 | 1 | 2020-05 |
張二 | 1 | 2020-05 |
張三 | 1 | 2020-04 |
張四 | 1 | 2020-03 |
李一 | 2 | 2020-05 |
李二 | 2 | 2020-04 |
李三 | 2 | 2020-02 |
2、按類型分組查詢每月新增用戶
使用如下sql統計每個月,每個類型的用戶新增人數。
SELECT
user_type,
create_month,
count( 1 ) AS cnt
FROM
t_user
GROUP BY
user_type,
create_month
查詢結果如下
user_type | create_month | cnt |
---|---|---|
1 | 2020-03 | 1 |
1 | 2020-04 | 1 |
1 | 2020-05 | 2 |
2 | 2020-02 | 1 |
2 | 2020-04 | 1 |
2 | 2020-05 | 1 |
3、初級進階:按類型分組查詢每個月累計用戶
使用如下sql統計每個月,每個類型的用戶累計用戶數。
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month
查詢結果如下
user_type | create_month | total |
---|---|---|
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
4、中級進階:在3的基礎上添加一個彙總的分類
在案例 3 基礎上,彙總每個月的 1 類和 2 類用戶爲 3 。
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month UNION ALL
SELECT
3 AS user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT create_month, count( 1 ) cnt FROM t_user GROUP BY create_month ) b
WHERE
b.create_month <= a.create_month
) AS total
FROM
t_user a
GROUP BY
create_month
查詢結果如下
user_type | create_month | total |
---|---|---|
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
3 | 2020-02 | 1 |
3 | 2020-03 | 2 |
3 | 2020-04 | 4 |
3 | 2020-05 | 7 |
5、高級進階:在3的基礎上補全缺失的月份
在案例 3 的基礎上,把分類 1 和 2 的缺失月份的數據補全爲 0。
思路爲:先把分類和月份做全連接,這樣就可以得出分類和月的所有排列組合;然後把數量對應到所在的分類和月份,沒有數量的使用 ifnull(total, 0) 補全爲 0。
SELECT
b.user_type,
b.create_month,
ifnull( a.total, 0 ) AS total
FROM
(
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month
) a
RIGHT JOIN (
SELECT
*
FROM
( SELECT user_type FROM t_user GROUP BY user_type ) a
LEFT JOIN ( SELECT create_month FROM t_user GROUP BY create_month ) b ON 1 = 1
) b ON a.user_type = b.user_type
AND a.create_month = b.create_month
ORDER BY
b.user_type,
b.create_month
查詢結果如下
user_type | create_month | total |
---|---|---|
1 | 2020-02 | 0 |
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-03 | 0 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
6、變態進階:在3的基礎上既補全缺失的月份,又添加彙總分類
把 4 和 5 結合起來,就可以兼顧補全缺失的月份和添加彙總分類。
SELECT
b.user_type,
b.create_month,
ifnull( a.total, 0 ) AS total
FROM
(
SELECT
user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT user_type, create_month, count( 1 ) cnt
FROM t_user GROUP BY user_type, create_month ) b
WHERE
b.create_month <= a.create_month
AND b.user_type = a.user_type
) AS total
FROM
t_user a
GROUP BY
user_type,
create_month
) a
RIGHT JOIN (
SELECT
*
FROM
( SELECT user_type FROM t_user GROUP BY user_type ) a
LEFT JOIN ( SELECT create_month FROM t_user GROUP BY create_month ) b ON 1 = 1
) b ON a.user_type = b.user_type
AND a.create_month = b.create_month UNION ALL
SELECT
3 AS user_type,
create_month,
(
SELECT
sum( cnt )
FROM
( SELECT create_month, count( 1 ) cnt FROM t_user GROUP BY create_month ) b
WHERE
b.create_month <= a.create_month
) AS total
FROM
t_user a
GROUP BY
create_month
ORDER BY
user_type,
create_month
查詢結果如下
user_type | create_month | total |
---|---|---|
1 | 2020-02 | 0 |
1 | 2020-03 | 1 |
1 | 2020-04 | 2 |
1 | 2020-05 | 4 |
2 | 2020-02 | 1 |
2 | 2020-03 | 0 |
2 | 2020-04 | 2 |
2 | 2020-05 | 3 |
3 | 2020-02 | 1 |
3 | 2020-03 | 2 |
3 | 2020-04 | 4 |
3 | 2020-05 | 7 |
7、總結
任何一個複雜需求都可以拆分成簡單的需求。
就像這個需求,如果一開始就讓我知道最終的SQL會這麼複雜,那我肯定繞道了。
🐒吼吼~~