From d023393bb2524f9623428c17e735e7818a3a2f97 Mon Sep 17 00:00:00 2001 From: butubb <1422726308@qq.com> Date: Mon, 12 Oct 2026 09:20:41 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E8=B4=A6=E5=8F=B7=E6=97=A5=E6=8A=A5=20?= =?UTF-8?q?SQL=EF=BC=88=E4=BB=8A=E5=A4=A9=E7=9A=84=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E6=83=85=E5=86=B5=20+=20=E4=B8=8E=E6=98=A8=E5=A4=A9=E6=AF=94?= =?UTF-8?q?=E5=AF=B9=20+=20=E6=8E=A8=E9=80=81=E6=9C=89=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=8F=91=E5=87=BA=E5=8E=BB=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两段,因为是两个维度:账号维度每个博主一行(粉丝/获赞/作品数及相对昨天的增减、 今天采到几条),任务维度看推送(推送是按任务的,塞进账号行会把同一个值重复 42 遍)。 推送那段的判定:last_notified_at 只在企业微信 errcode=0 之后才写,所以拿它和一串 run 的结束时间去比,落在两分钟内那轮就是**最后一次真正发出去的那轮**;那之后本该推 却没推的轮次就是漏掉的。**更早的失败无法回溯** —— 失败没留任何记录,而 last_notified_at 只有一个值,会被后来的成功覆盖。文件里写明了这个边界。 实测:42 个账号;粉丝 41 平 1 掉、主页作品数全平;推送最近一次成功是 run 76,之后 0 次漏掉。 --- docs/queries/account-daily-report.sql | 122 ++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 docs/queries/account-daily-report.sql diff --git a/docs/queries/account-daily-report.sql b/docs/queries/account-daily-report.sql new file mode 100644 index 0000000..cc58b1e --- /dev/null +++ b/docs/queries/account-daily-report.sql @@ -0,0 +1,122 @@ +-- 抖音账号日报:今天的账号情况 + 与昨天比对 + 推送有没有发出去 +-- +-- 分两段,因为它们是**两个维度**,硬塞进一张表会难看: +-- +-- 【一】账号维度:每个博主一行,今天的账号指标、相对昨天的增减、今天的作品情况。 +-- 【二】任务维度:推送到底有没有发出去,以及从上次成功之后漏了几次。 +-- +-- 口径说明: +-- * 「今天」= 该博主**最新一条**账号快照;「昨天」= 最新一条**日期早于今天**的快照。 +-- 快照是一轮一条(任务一天跑好几轮),所以按日期取,不按轮次。 +-- * 差值为 NULL 表示**没有可比的昨天**(昨天没跑或没采到这个号),不是 0。 +-- * 时间戳是毫秒,日期换算统一走 FROM_UNIXTIME(x/1000)。 +-- * MySQL 5.7 没有窗口函数,所以「最新一条」还是相关子查询。 + +-- =========================================================================== +-- 【一】账号日报 +-- =========================================================================== +SELECT + al.alias AS 博主备注, + COALESCE(cur.nickname, yest.nickname, '') AS 博主昵称, + cur.fans AS 粉丝数, + cur.fans - yest.fans AS 粉丝增减, + cur.total_favorited AS 总获赞, + cur.total_favorited - yest.total_favorited AS 获赞增减, + cur.works_count AS 主页作品数, + cur.works_count - yest.works_count AS 作品数增减, + (SELECT COUNT(*) FROM monitor_note n + JOIN monitor_task tn ON tn.id = n.task_id + WHERE tn.platform = 'dy' AND n.creator_hash = base.creator_hash) AS 监控中作品, + (SELECT COUNT(*) FROM monitor_note n + JOIN monitor_task tn ON tn.id = n.task_id + WHERE tn.platform = 'dy' AND n.creator_hash = base.creator_hash + AND n.first_seen_at >= UNIX_TIMESTAMP(CURDATE()) * 1000) AS 今日新采到, + FROM_UNIXTIME((SELECT MAX(n.published_at) FROM monitor_note n + JOIN monitor_task tn ON tn.id = n.task_id + WHERE tn.platform = 'dy' + AND n.creator_hash = base.creator_hash) / 1000) AS 最近作品发布, + FROM_UNIXTIME(cur.captured_at / 1000) AS 最近快照, + CASE WHEN yest.id IS NULL THEN '昨天无快照' ELSE '有' END AS 昨日可比 +FROM ( + -- 账号清单:作品里出现过的 ∪ 有账号资料的(含一条作品都没有的号) + SELECT n1.creator_hash FROM monitor_note n1 + JOIN monitor_task t1 ON t1.id = n1.task_id WHERE t1.platform = 'dy' + UNION + SELECT s1.creator_hash FROM monitor_creator_stat s1 + JOIN monitor_task t2 ON t2.id = s1.task_id WHERE t2.platform = 'dy' +) base +-- 今天(最新一条快照) +LEFT JOIN monitor_creator_stat cur + ON cur.id = (SELECT s.id FROM monitor_creator_stat s + JOIN monitor_task t3 ON t3.id = s.task_id + WHERE t3.platform = 'dy' AND s.creator_hash = base.creator_hash + ORDER BY s.captured_at DESC, s.id DESC LIMIT 1) +-- 昨天(最新一条日期早于今天的快照) +LEFT JOIN monitor_creator_stat yest + ON yest.id = (SELECT s.id FROM monitor_creator_stat s + JOIN monitor_task t4 ON t4.id = s.task_id + WHERE t4.platform = 'dy' AND s.creator_hash = base.creator_hash + AND DATE(FROM_UNIXTIME(s.captured_at / 1000)) < CURDATE() + ORDER BY s.captured_at DESC, s.id DESC LIMIT 1) +LEFT JOIN monitor_creator_alias al + ON al.platform = 'dy' AND al.creator_hash = base.creator_hash +ORDER BY (cur.fans - yest.fans) IS NULL, cur.fans - yest.fans DESC, cur.fans DESC; + + +-- =========================================================================== +-- 【二】推送有没有发出去(任务维度) +-- =========================================================================== +-- +-- 判定办法,两步: +-- +-- 1. `last_notified_at` **只在企业微信返回 errcode=0 之后才写**,所以它是「最近一次 +-- 推送成功的时刻」。推送就发生在那一轮跑完之后几秒内 —— 拿它去和一串 run 的结束 +-- 时间比,落在两分钟内的那一轮,就是**最后一次真正发出去的那轮**。 +-- 2. 那一轮**之后**、本该推送却没有的轮次,就是漏掉的。`漏掉几次` 就是这么数的。 +-- +-- 为什么不能更精确:失败的推送没有留下任何记录(原因只打到容器 stdout),`last_notified_at` +-- 也只有一个值,会被后来的成功覆盖。要逐轮可查得让它落一条事件或一列原因 —— 那是改代码, +-- 不是这条 SQL 能补的。 +-- +-- 「本该推送」= 那轮 new_notes > 0,或者出了失败类事件(和 notify.build_run_message 的 +-- 判断一致:它按任务上的两个开关过滤事件,没有可推的就返回 None、压根不发)。 +SELECT + t.id AS 任务, + t.name AS 名称, + t.notify_enabled AS 推新作品, + t.notify_failures AS 推异常, + FROM_UNIXTIME(t.last_notified_at / 1000) AS 上次推送成功于, + hit.run_id AS 成功的是这轮, + -- 没有匹配行(= 一次没漏)时 LEFT JOIN 给的是 NULL,直接显示会是一片空白, + -- 分不清「零」和「查不到」。这里补成 0。 + COALESCE(leaks.漏掉几次, 0) AS 那之后漏掉, + leaks.最近漏掉的那轮 AS 最近漏掉的轮次, + FROM_UNIXTIME(leaks.最近漏掉的时间) AS 它结束于 +FROM monitor_task t +-- 找「推送成功时刻」对应的那一轮:结束时间落在它之前两分钟内、且最近的那一轮。 +LEFT JOIN ( + SELECT r.task_id, MAX(r.id) AS run_id + FROM monitor_run r + JOIN monitor_task t5 ON t5.id = r.task_id + WHERE t5.last_notified_at IS NOT NULL + AND r.finished_at <= t5.last_notified_at + AND t5.last_notified_at - r.finished_at <= 120000 + GROUP BY r.task_id +) hit ON hit.task_id = t.id +-- 上次成功之后,本该推送却没推的轮次。 +LEFT JOIN ( + SELECT r.task_id, + COUNT(*) AS 漏掉几次, + MAX(r.id) AS 最近漏掉的那轮, + MAX(r.finished_at) AS 最近漏掉的时间 + FROM monitor_run r + JOIN monitor_task t6 ON t6.id = r.task_id + WHERE r.finished_at > COALESCE(t6.last_notified_at, 0) + AND (r.new_notes > 0 + OR EXISTS (SELECT 1 FROM monitor_event e + WHERE e.run_id = r.id + AND e.type IN ('suspected_auth_failure', 'run_failed', 'no_data_found'))) + GROUP BY r.task_id +) leaks ON leaks.task_id = t.id +WHERE t.platform = 'dy' +ORDER BY t.id;