fix(scripts): backfill_group_members 窗口函数化过 MySQL only_full_group_by(单库收敛)

max(timestamp)+裸 sender_name 的 GROUP BY 是 SQLite 方言行为,MySQL 8 默认
only_full_group_by 直接 1055 拒绝——2026-08-27 单库收敛(本地+调度器同连
Docker MySQL)时在 MySQL 上实跑触发。改 row_number() OVER 取每 (room_id,
talker_id) 最新一行的 sender_name + count() OVER 计数:两库通用,且语义从
「碰巧取 max 行」变为严格最新一条。另 gitignore 补 *.db.retired
(smoke.db 退役改名后 *.db 规则罩不住)。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
浅念
2026-08-27 14:51:05 +08:00
parent 7e8624674e
commit b2ccbee1ab
2 changed files with 16 additions and 6 deletions
+1
View File
@@ -22,6 +22,7 @@ media/
# Local scratch DBs used for Alembic autogenerate (see migrations/env.py)
*.db
*.db.retired
*.sqlite
*.sqlite3
.playwright-mcp/
+15 -6
View File
@@ -26,13 +26,22 @@ from app.storage.repos import _upsert_one
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("backfill_group_members")
# max(timestamp) 裸列:SQLite 保证 sender_name 取自 timestamp 最大的那行
# row_number() 窗口取 timestamp 最新一行的 sender_name:MySQL 8 的
# only_full_group_by 拒绝 SQLite 式「max()+裸列」(1055),窗口写法两库通用
# 且语义严格(最新一条的昵称),不依赖方言碰巧行为
SQL = sa_text("""
SELECT room_id, talker_id, sender_name, max(timestamp) AS last_ts, count(*) AS msg_count
FROM message
WHERE channel = 'wechat' AND room_id IS NOT NULL AND room_id != ''
AND talker_id IS NOT NULL AND talker_id != ''
GROUP BY room_id, talker_id
SELECT room_id, talker_id, sender_name, last_ts, msg_count
FROM (
SELECT room_id, talker_id, sender_name, timestamp AS last_ts,
count(*) OVER (PARTITION BY room_id, talker_id) AS msg_count,
row_number() OVER (
PARTITION BY room_id, talker_id ORDER BY timestamp DESC
) AS rn
FROM message
WHERE channel = 'wechat' AND room_id IS NOT NULL AND room_id != ''
AND talker_id IS NOT NULL AND talker_id != ''
) t
WHERE rn = 1
""")