"""Read-only: per-column stats for a table. usage: inspect_erp_table.py TABLE [regex-of-cols]""" import os, re, sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "agent" / "src")) from gti_fiscal_agent.firebird_reader import _connect for l in (ROOT / ".env").read_text(encoding="utf-8").splitlines(): if "=" in l and not l.startswith("#"): k, _, v = l.partition("="); os.environ.setdefault(k.strip(), v.strip()) t = sys.argv[1]; rx = re.compile(sys.argv[2], re.I) if len(sys.argv) > 2 else None conn = _connect(os.environ); cur = conn.cursor() cur.execute("select trim(rf.RDB$FIELD_NAME), f.RDB$FIELD_TYPE, coalesce(f.RDB$CHARACTER_LENGTH,f.RDB$FIELD_LENGTH) from RDB$RELATION_FIELDS rf join RDB$FIELDS f on f.RDB$FIELD_NAME=rf.RDB$FIELD_SOURCE where rf.RDB$RELATION_NAME=? order by rf.RDB$FIELD_POSITION", (t,)) for name, ty, ln in cur.fetchall(): if rx and not rx.search(name): continue if ty in (261,): # blob print(f"{name} BLOB"); continue cur.execute(f'select "{name}", count(*) from "{t}" group by "{name}" order by 2 desc rows 1 to 400') rows = cur.fetchall() nn = sum(c for v, c in rows if v is not None) top = [(str(v).strip()[:24] if v is not None else None, c) for v, c in rows[:5]] print(f"{name} t{ty}/{ln} nn={nn} distinct{'>=' if len(rows)>=400 else '='}{len(rows)} {top}") conn.close()