計算「昨日登入用戶中,有多少在前日也登入過」的留存率。核心表:
visitor_user:用戶登入記錄(shop_id, device, a_token, off_time)report_user_login_day_detail:每日登入明細report_user_remain_day_detail:留存明細(含狀態欄位)report_user_remain_day:留存率匯總CREATE FUNCTION statics_user_remain_day() RETURNS TINYINT
BEGIN
DECLARE _yesterday DATE;
DECLARE _before_yesterday DATE;
DECLARE _is_success TINYINT;
SELECT DATE_SUB(DATE_FORMAT(NOW(), '%Y-%m-%d'), INTERVAL 1 DAY) INTO _yesterday;
SELECT DATE_SUB(DATE_FORMAT(NOW(), '%Y-%m-%d'), INTERVAL 2 DAY) INTO _before_yesterday;
-- 步驟1:前日用戶寫入留存明細
SET _is_success = get_user_remain_day(_before_yesterday);
-- 步驟2:昨日用戶匹配更新狀態
SET _is_success = update_user_status_remain(_yesterday);
-- 步驟3:統計留存率寫入匯總
SET _is_success = insert_user_remain_day(_yesterday);
RETURN _is_success;
END
CREATE FUNCTION get_user_remain_day(_day_time DATE) RETURNS INT
BEGIN
DECLARE stopFlag INT DEFAULT 0;
DECLARE _shop_id INT DEFAULT 0;
DECLARE _device VARCHAR(50);
DECLARE _a_token CHAR(64) DEFAULT '-1';
DECLARE cur1 CURSOR FOR
SELECT shop_id, device, a_token
FROM report_user_login_day_detail
WHERE day_time = _day_time;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1;
OPEN cur1;
FETCH cur1 INTO _shop_id, _device, _a_token;
WHILE stopFlag = 0 DO
INSERT INTO report_user_remain_day_detail (id, shop_id, day_time, device, a_token)
VALUES (UUID(), _shop_id, _day_time, _device, _a_token);
FETCH cur1 INTO _shop_id, _device, _a_token;
END WHILE;
CLOSE cur1;
RETURN 1;
END
CREATE FUNCTION update_user_status_remain(_day_time DATE) RETURNS TINYINT
BEGIN
DECLARE stopFlag INT DEFAULT 0;
DECLARE _device VARCHAR(50);
DECLARE _a_token CHAR(64) DEFAULT '-1';
DECLARE cur1 CURSOR FOR
SELECT device, a_token
FROM report_user_login_day_detail
WHERE day_time = _day_time;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1;
OPEN cur1;
FETCH cur1 INTO _device, _a_token;
WHILE stopFlag = 0 DO
UPDATE report_user_remain_day_detail
SET status_remain = 1, day_time = _day_time
WHERE device = _device;
FETCH cur1 INTO _device, _a_token;
END WHILE;
CLOSE cur1;
RETURN 1;
END
CREATE FUNCTION insert_user_remain_day(_day_time DATE) RETURNS TINYINT
BEGIN
DECLARE stopFlag INT DEFAULT 0;
DECLARE _status_remain TINYINT;
DECLARE _remain_count INT DEFAULT 0;
DECLARE _all_count INT DEFAULT 0;
DECLARE _temp_count INT DEFAULT 0;
DECLARE cur2 CURSOR FOR
SELECT status_remain, COUNT(device)
FROM report_user_remain_day_detail
WHERE day_time = _day_time
GROUP BY status_remain;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET stopFlag = 1;
OPEN cur2;
FETCH cur2 INTO _status_remain, _remain_count;
WHILE stopFlag = 0 DO
IF _status_remain = 1 THEN
SET _temp_count = _remain_count;
SET _all_count = _all_count + _remain_count;
ELSE
SET _all_count = _all_count + _remain_count;
END IF;
FETCH cur2 INTO _status_remain, _remain_count;
END WHILE;
CLOSE cur2;
INSERT INTO report_user_remain_day (id, day_time, remain_count, remain_percent_day, type_client)
VALUES (UUID(), _day_time, _temp_count, _temp_count / _all_count, 0);
RETURN 1;
END
CREATE EVENT ev_daily_remain_stat ON SCHEDULE EVERY 1 DAY STARTS '2024-01-01 04:00:00' DO CALL statics_user_remain_day();
INSERT ... SELECT、UPDATE ... JOIN 批次操作report_user_login_day_detail(day_time, device)、report_user_remain_day_detail(device, day_time)-- 步驟1
INSERT INTO report_user_remain_day_detail (id, shop_id, day_time, device, a_token, status_remain)
SELECT UUID(), shop_id, _before_yesterday, device, a_token, 0
FROM report_user_login_day_detail
WHERE day_time = _before_yesterday;
-- 步驟2
UPDATE report_user_remain_day_detail r
JOIN report_user_login_day_detail l ON r.device = l.device
SET r.status_remain = 1, r.day_time = _yesterday
WHERE l.day_time = _yesterday;
-- 步驟3
INSERT INTO report_user_remain_day (id, day_time, remain_count, remain_percent_day, type_client)
SELECT UUID(), _yesterday,
SUM(CASE WHEN status_remain = 1 THEN 1 ELSE 0 END),
SUM(CASE WHEN status_remain = 1 THEN 1 ELSE 0 END) / COUNT(*),
0
FROM report_user_remain_day_detail
WHERE day_time IN (_before_yesterday, _yesterday);