-- 链接 LINK: https://www.nowcoder.com/practice/6765b4a4f260455bae513a60b6eed0af?tpId=268&tqId=2285345&ru=/exam/oj&qru=/ta/sql-factory-interview/question-ranking&sourceUrl=%2Fexam%2Foj%3Ftab%3DSQL%25E7%25AF%2587%26topicId%3D268
-- 标题 TITLE: SQL10 统计活跃间隔对用户分级结果
-- 日期 DATE: 2022-01-24 08:31:44
/* NOTE 让表基于已有数据来新增列的方法(常见)
- group by
- windows function
- join
- string split
**/
-- 题解 SQL CASE 1
SELECT T1.user_grade,
(CASE WHEN T2.num IS NULL THEN 0 ELSE ROUND(T2.num / SUM(T2.num) OVER(), 2) END) ratio
FROM (
-- 字典,用户等级名称及其对应的编码
SELECT '忠实用户' user_grade, 1 code
UNION
SELECT '新晋用户' user_grade, 0 code
UNION
SELECT '沉睡用户' user_grade, 2 code
UNION
SELECT '流失用户' user_grade, 3 code
) T1
LEFT JOIN (
-- 统计每个编码值有多少位用户
SELECT (CASE WHEN code >= 10 THEN 0 ELSE code END) code,
COUNT(1) num
FROM (
-- 编码用户,且每名用户仅有一条编码数据,编码值对应用户等级
SELECT uid, SUM(CASE
WHEN ranks = 1 AND timediff < 7 THEN 10
WHEN ranks = num AND timediff < 7 THEN 1
WHEN ranks = num AND timediff < 30 THEN 2
WHEN ranks = num THEN 3
ELSE 0 END) code
FROM (
SELECT uid, time,
TIMESTAMPDIFF(day, time, MAX(time) OVER()) timediff,
row_number() OVER(PARTITION BY uid ORDER BY time) ranks,
COUNT(1) OVER(PARTITION BY uid) num
FROM (
SELECT uid, DATE_FORMAT(in_time, '%Y-%m-%d') time FROM tb_user_log
UNION
SELECT uid, DATE_FORMAT(out_time, '%Y-%m-%d') time FROM tb_user_log
) TA
GROUP BY uid, time
) TB
-- 找到每名用户最早的注册时间和最新的活跃时间
WHERE ranks = 1 OR ranks = num
GROUP BY uid
) TC
GROUP BY (CASE WHEN code >= 10 THEN 0 ELSE code END)
) T2 ON T2.code = T1.code
;
-- 题解 SQL CASE 2
SELECT T1.user_grade,
(CASE WHEN T2.num IS NULL THEN 0 ELSE ROUND(T2.num / SUM(T2.num) OVER(), 2) END) ratio
FROM (
-- 字典,用户等级名称及其对应的编码,
SELECT '忠实用户' user_grade, 1 code
UNION
SELECT '新晋用户' user_grade, 0 code
UNION
SELECT '沉睡用户' user_grade, 2 code
UNION
SELECT '流失用户' user_grade, 3 code
) T1
LEFT JOIN (
SELECT code, COUNT(1) num
FROM (
SELECT uid,
(CASE
WHEN MAX(timediff) < 7 THEN 0
WHEN MIN(timediff) < 7 THEN 1
WHEN MIN(timediff) < 30 THEN 2
ELSE 3 END) code
FROM (
SELECT uid,
TIMESTAMPDIFF(day, time, MAX(time) OVER()) timediff
FROM (
SELECT uid, DATE_FORMAT(in_time, '%Y-%m-%d') time FROM tb_user_log
UNION
SELECT uid, DATE_FORMAT(out_time, '%Y-%m-%d') time FROM tb_user_log
) TA
GROUP BY uid, time
) TB
GROUP BY uid
) TC
GROUP BY code
) T2 ON T2.code = T1.code
;
-- 数据 DATA ===================================================
DROP TABLE IF EXISTS tb_user_log;
CREATE TABLE tb_user_log (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
uid INT NOT NULL COMMENT '用户ID',
artical_id INT NOT NULL COMMENT '视频ID',
in_time datetime COMMENT '进入时间',
out_time datetime COMMENT '离开时间',
sign_in TINYINT DEFAULT 0 COMMENT '是否签到'
) CHARACTER SET utf8 COLLATE utf8_bin;
-- 最新日期为 2021-11-04
INSERT INTO tb_user_log(uid, artical_id, in_time, out_time, sign_in) VALUES
(109, 9001, '2021-08-31 10:00:00', '2021-08-31 10:00:09', 0),
(109, 9002, '2021-11-04 11:00:55', '2021-11-04 11:00:59', 0),
(108, 9001, '2021-09-01 10:00:01', '2021-09-01 10:01:50', 0),
(108, 9001, '2021-11-03 10:00:01', '2021-11-03 10:01:50', 0),
(104, 9001, '2021-11-02 10:00:28', '2021-11-02 10:00:50', 0),
(104, 9003, '2021-09-03 11:00:45', '2021-09-03 11:00:55', 0),
(105, 9003, '2021-11-03 11:00:53', '2021-11-03 11:00:59', 0),
(102, 9001, '2021-10-30 10:00:00', '2021-10-30 10:00:09', 0),
(103, 9001, '2021-10-21 10:00:00', '2021-10-21 10:00:09', 0),
-- (101, 9001, '2021-10-01 10:00:00', '2021-10-01 10:00:42', 1),
(110, 0, '2021-11-01 10:00:00', '2021-11-01 10:00:42', 1),
(110, 9001, '2021-11-03 10:00:00', '2021-11-03 10:00:42', 1),
(110, 9001, '2021-11-02 00:00:00', '2021-11-03 00:00:42', 1),
(111, 9001, '2021-05-01 10:00:00', '2021-05-01 10:00:42', 1),
(111, 9002, '2021-10-04 11:00:55', '2021-10-04 11:00:59', 0),
(111, 9002, '2021-10-28 10:00:00', '2021-10-28 10:00:42', 0),
(112, 9002, '2021-11-03 00:00:00', '2021-11-04 00:00:42', 0),
(113, 9002, '2021-10-05 00:00:00', '2021-10-06 00:00:42', 0),
(113, 9002, '2021-11-03 00:00:00', '2021-11-04 00:00:42', 0),
(114, 9002, '2021-10-05 00:00:00', '2021-10-06 00:00:42', 0),
(114, 9002, '2021-11-03 00:00:00', '2021-11-04 00:00:42', 0),
(115, 9002, '2021-05-06 00:00:00', '2021-05-06 00:00:42', 0),
(115, 9002, '2021-10-28 10:00:00', '2021-10-28 10:00:42', 0);