#!/usr/bin/env python3 """清理 strategy_trade_snapshots 重复行(同计划 + 同结果仅保留 id 最大的一条)。 用法(在实例目录,如 crypto_monitor_gate_bot): python ../scripts/dedupe_strategy_snapshots.py python ../scripts/dedupe_strategy_snapshots.py --db crypto.db """ from __future__ import annotations import argparse import os import sqlite3 import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) from strategy_snapshot_lib import dedupe_strategy_snapshots, init_strategy_snapshot_table # noqa: E402 def main() -> int: parser = argparse.ArgumentParser(description="Dedupe strategy_trade_snapshots rows.") parser.add_argument( "--db", default=os.getenv("DB_PATH", "crypto.db"), help="SQLite database path (default: DB_PATH or crypto.db)", ) parser.add_argument("--dry-run", action="store_true", help="Count only, do not delete") args = parser.parse_args() db_path = Path(args.db) if not db_path.is_file(): print(f"DB not found: {db_path}", file=sys.stderr) return 1 conn = sqlite3.connect(str(db_path)) conn.row_factory = sqlite3.Row init_strategy_snapshot_table(conn) before = conn.execute("SELECT COUNT(*) AS c FROM strategy_trade_snapshots").fetchone()["c"] dup_groups = conn.execute( """SELECT strategy_type, source_id, result_label, COUNT(*) AS n FROM strategy_trade_snapshots GROUP BY strategy_type, source_id, result_label HAVING n > 1 ORDER BY n DESC""" ).fetchall() extra = sum(int(r["n"]) - 1 for r in dup_groups) print(f"snapshots total={before}, duplicate rows to remove={extra}, groups={len(dup_groups)}") for r in dup_groups[:20]: print( f" {r['strategy_type']} plan={r['source_id']} " f"{r['result_label']} x{r['n']}" ) if args.dry_run: conn.close() return 0 removed = dedupe_strategy_snapshots(conn) conn.commit() after = conn.execute("SELECT COUNT(*) AS c FROM strategy_trade_snapshots").fetchone()["c"] conn.close() print(f"removed={removed}, remaining={after}") return 0 if __name__ == "__main__": raise SystemExit(main())