"""用 pymysql 直接跑改后的 SQL(与 routes/energy_stats.rs 中的语句逐字对应, 唯一差别:Rust 用 `?` 占位符 + bind,这里把测试常量(org_id=1 / range=30) 直接内联,以保持 SQL 文本与 Rust 生成的完全一致,并避开 pymysql 的 `%` 转义)。 只做只读 SELECT,不改任何数据。 """ import pymysql DB = dict(host="10.8.0.252", user="root", password="Hbhyg731024@", database="pms_dev") conn = pymysql.connect(**DB) cur = conn.cursor() TODAY = "cr.start_time >= CURDATE() AND cr.start_time < CURDATE() + INTERVAL 1 DAY" THIS_MONTH = ( "cr.start_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') " "AND cr.start_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01')" ) SUM_E = "CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED)" CNT_E = "CAST(COUNT(*) AS SIGNED)" # 组织隔离片段(与 auth::org_condition(user, "cr") / 各处的内联版本逐字一致) ORG_WHERE_1 = (" AND cr.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p " "ON c.project_id = p.id WHERE p.organization_id = 1)") ORG_WHERE_999 = (" AND cr.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p " "ON c.project_id = p.id WHERE p.organization_id = 999)") ORG_WHERE_NONE = " AND 1=0" ORG_JOIN_1 = " JOIN projects p ON c.project_id = p.id AND p.organization_id = 1" ORG_JOIN_NONE = " JOIN projects p ON 1=0" PROJ_ORG_1 = " WHERE p.organization_id = 1" PROJ_ORG_NONE = " WHERE 1=0" def show(title, sql): print("=" * 78) print(f"### {title}") print("SQL> " + " ".join(sql.split())) cur.execute(sql) cols = [d[0] for d in cur.description] print(" " + " | ".join(cols)) rows = cur.fetchall() for r in rows: print(" " + " | ".join(str(v) for v in r)) print(f" => {len(rows)} 行") return rows # ---------------------------------------------------------------- 原始数据 show( "0. charge_records 现有行(cabinet_id / start_time / energy)", "SELECT cr.id, cr.cabinet_id, c.name, cr.start_time, cr.end_time, cr.energy " "FROM charge_records cr LEFT JOIN cabinets c ON cr.cabinet_id = c.id " "ORDER BY cr.start_time DESC, cr.cabinet_id", ) # ---------------------------------------------------------------- ① summary show("1a. summary today(总管理员,无组织条件)", f"SELECT {SUM_E} FROM charge_records cr WHERE {TODAY}") show("1b. summary month(总管理员)", f"SELECT {SUM_E} FROM charge_records cr WHERE {THIS_MONTH}") show("1c. summary today(组织 1 隔离)", f"SELECT {SUM_E} FROM charge_records cr WHERE {TODAY}{ORG_WHERE_1}") show("1d. summary today(不存在组织 999 ⇒ 必须为空集 0)", f"SELECT {SUM_E} FROM charge_records cr WHERE {TODAY}{ORG_WHERE_999}") show("1e. summary today(客户侧无组织 ⇒ AND 1=0)", f"SELECT {SUM_E} FROM charge_records cr WHERE {TODAY}{ORG_WHERE_NONE}") # ---------------------------------------------------------------- ② cabinets RANK = f"""SELECT c.id, COALESCE(c.abstract_id, '') AS abstract_id, c.name, CAST(COALESCE(cr_today.today_total, 0) AS SIGNED) AS today_energy, CAST(COALESCE(cr_month.month_total, 0) AS SIGNED) AS month_energy FROM cabinets c LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE {TODAY} GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE {THIS_MONTH} GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id {{org}} ORDER BY today_energy DESC, c.id ASC""" show("2a. cabinets 排行(总管理员)", RANK.format(org="")) show("2b. cabinets 排行(组织 1 隔离)", RANK.format(org=ORG_JOIN_1)) show("2c. cabinets 排行(客户侧无组织 ⇒ JOIN projects p ON 1=0 ⇒ 空)", RANK.format(org=ORG_JOIN_NONE)) # ---------------------------------------------------------------- ③ projects PROJ = f"""SELECT p.id, p.name, COUNT(DISTINCT c.id) AS cabinet_count, CAST(COALESCE(SUM(cr_today.today_total), 0) AS SIGNED) AS today_energy, CAST(COALESCE(SUM(cr_month.month_total), 0) AS SIGNED) AS month_energy FROM projects p LEFT JOIN cabinets c ON p.id = c.project_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS today_total FROM charge_records cr WHERE {TODAY} GROUP BY cr.cabinet_id ) cr_today ON c.id = cr_today.cabinet_id LEFT JOIN ( SELECT cr.cabinet_id, SUM(cr.energy) AS month_total FROM charge_records cr WHERE {THIS_MONTH} GROUP BY cr.cabinet_id ) cr_month ON c.id = cr_month.cabinet_id {{org}} GROUP BY p.id, p.name ORDER BY today_energy DESC, p.id ASC""" show("3a. projects 统计(总管理员)", PROJ.format(org="")) show("3b. projects 统计(组织 1 隔离)", PROJ.format(org=PROJ_ORG_1)) show("3c. projects 统计(客户侧无组织 ⇒ WHERE 1=0)", PROJ.format(org=PROJ_ORG_NONE)) # ---------------------------------------------------------------- ④ trend TREND = f"""SELECT {{period}} AS period, {SUM_E} AS energy, {CNT_E} AS charge_count FROM charge_records cr WHERE cr.start_time >= DATE_SUB(CURDATE(), INTERVAL {{rng}} DAY){{org}} GROUP BY period ORDER BY period ASC""" show("4a. trend granularity=day(总管理员, range=30)", TREND.format(period="DATE_FORMAT(cr.start_time, '%Y-%m-%d')", rng=30, org="")) show("4b. trend granularity=week(range=30)", TREND.format(period="DATE_FORMAT(cr.start_time, '%Y-%u')", rng=30, org="")) show("4c. trend granularity=month(range=30)", TREND.format(period="DATE_FORMAT(cr.start_time, '%Y-%m')", rng=30, org="")) show("4d. trend granularity=day(组织 1 隔离)", TREND.format(period="DATE_FORMAT(cr.start_time, '%Y-%m-%d')", rng=30, org=ORG_WHERE_1)) show("4e. trend range=-5 **未经夹紧**(DATE_SUB 变成未来 5 天 ⇒ 必然空)", TREND.format(period="DATE_FORMAT(cr.start_time, '%Y-%m-%d')", rng=-5, org="")) show("4f. trend range=-5 经 clamp_range ⇒ 1(应含昨天+今天)", TREND.format(period="DATE_FORMAT(cr.start_time, '%Y-%m-%d')", rng=1, org="")) # ---------------------------------------------------------------- ⑤ export EXPORT = f"""SELECT DATE(cr.start_time) AS stat_date, cr.cabinet_id, c.name AS cabinet_name, p.name AS project_name, CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) AS total_energy, {CNT_E} AS charge_count FROM charge_records cr LEFT JOIN cabinets c ON cr.cabinet_id = c.id LEFT JOIN projects p ON c.project_id = p.id WHERE cr.start_time IS NOT NULL{{extra}} GROUP BY DATE(cr.start_time), cr.cabinet_id, c.name, p.name ORDER BY stat_date DESC, cr.cabinet_id ASC LIMIT 100000""" show("5a. export 明细(无筛选)", EXPORT.format(extra="")) show("5b. export 明细(end_date=今天必须含当天)", EXPORT.format( extra=" AND cr.start_time >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)" " AND cr.start_time < DATE_ADD(CURDATE(), INTERVAL 1 DAY)" )) show("5c. export 明细(组织 1 隔离)", EXPORT.format(extra=ORG_WHERE_1)) # ---------------------------------------------------------------- 对照 show("9. 对照:energy_stats 表行数(已无人读写)", "SELECT COUNT(*) AS energy_stats_rows FROM energy_stats") cur.close() conn.close()