#!/usr/bin/env python3 """ 在达梦数据库机器上运行,查看各表结构,用于编写 config 中的 db.query 用法: python scripts/inspect_dm_tables.py 密码获取顺序:环境变量 DM_PASS > 下方 LOCAL_PASSWORD 若使用 LOCAL_PASSWORD,切勿提交到 git!可执行:git update-index --assume-unchanged scripts/inspect_dm_tables.py """ import os import sys # 可选:在此直接填写密码(仅限本机调试,切勿提交到版本库!) LOCAL_PASSWORD = None # 例如: "你的密码" # 支持从项目根或 scripts 目录运行 _script_dir = os.path.dirname(os.path.abspath(__file__)) _project_root = os.path.dirname(_script_dir) XLSX_PATH = os.path.join(_project_root, "1125暂时属性需求(1).xlsx") if _project_root not in sys.path: sys.path.insert(0, _project_root) try: import dmPython except ImportError: print("请先安装: pip install dmPython") sys.exit(1) CONFIG = { "user": os.getenv("DM_USER", "SYSDBA"), "password": os.getenv("DM_PASS") or LOCAL_PASSWORD, "server": os.getenv("DM_HOST", "127.0.0.1"), "port": int(os.getenv("DM_PORT", "9080")), "schema": "SYSDBA", } TABLES = [ "OBJECTIVE_INSTANCE", "METRIC_VALUE_INSTANCE", "METRIC_PROFILE", "ROLE_PROFILE", "TASK_PROFILE", "RELATION_INSTANCE", "RES_FORMAL_ANALYSIS", ] def _load_required_attrs(xlsx_path: str) -> list: """从 1125暂时属性需求(1).xlsx 解析需求属性列表(一条=一个属性)""" if not os.path.exists(xlsx_path): print(f" (未找到 {xlsx_path},使用内置属性列表)") return _DEFAULT_REQ_ATTRS try: import pandas as pd df = pd.read_excel(xlsx_path, header=None) attrs = [] for _, row in df.iterrows(): v0 = row[0] v2 = str(row[2]) if len(row) > 2 and pd.notna(row[2]) else "" if pd.notna(v0) and str(v0).strip() and str(v0) != "属性代码名称": attrs.append(str(v0).strip()) if ":" in v2: sub = v2.split(":")[0].strip() if sub and sub not in attrs: attrs.append(sub) return attrs if attrs else _DEFAULT_REQ_ATTRS except Exception as e: print(f" (解析 xlsx 失败: {e},使用内置属性列表)") return _DEFAULT_REQ_ATTRS # xlsx 解析失败时的兜底属性列表(来自 1125暂时属性需求) _DEFAULT_REQ_ATTRS = [ "f_IC", "f_IA", "f_CS", "f_IT", "f_DP", "f_CPS", "f_CC", "position", "effective_radius", "response_time", "cycle_time", "data_age", "time_window", "core_performance", "survivability", "mtbf", "protocol_list", "format_list", "interface_list", "security_level", "org_unit", "nation", "history_success", ] def main(): if not CONFIG["password"]: print("请设置环境变量 DM_PASS 或在脚本中填写 LOCAL_PASSWORD") sys.exit(1) try: conn = dmPython.connect( user=CONFIG["user"], password=CONFIG["password"], server=CONFIG["server"], port=CONFIG["port"], schema=CONFIG["schema"], ) except Exception as e: print(f"连接失败: {e}") sys.exit(1) cursor = conn.cursor() print("=" * 60) print("达梦表结构(用于编写 config/db.query)") print("=" * 60) for table in TABLES: try: cursor.execute(f'SELECT * FROM "{CONFIG["schema"]}"."{table}" WHERE 1=0') cols = [d[0] for d in cursor.description] print(f"\n【{table}】 列: {cols}") except Exception as e: print(f"\n【{table}】 查询失败: {e}") # 专门查询 METRIC_VALUE_INSTANCE 的字段详情(含完整属性) mvi_table = "METRIC_VALUE_INSTANCE" print("\n" + "=" * 80) print(f"【{mvi_table}】 字段详情(完整属性)") print("=" * 80) try: # 查询 DBA_TAB_COLUMNS 全部常用属性(达梦兼容) cursor.execute( """ SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT, COLUMN_ID FROM DBA_TAB_COLUMNS WHERE OWNER = ? AND TABLE_NAME = ? ORDER BY COLUMN_ID """, (CONFIG["schema"], mvi_table), ) mvi_cols = cursor.fetchall() # 表头 fmt = " {:<18} {:<16} {:<10} {:<10} {:<8} {:<10} {:<20}" print(fmt.format("列名", "数据类型", "长度", "精度", "标度", "可空", "默认值")) print(" " + "-" * 90) for row in mvi_cols: col_name, data_type, data_length, data_precision, data_scale, nullable, data_default, col_id = row # 类型显示:数值型用 类型(精度,标度),字符型用 类型(长度) if data_precision is not None: type_str = f"{data_type}({data_precision},{data_scale or 0})" elif data_length and data_type.upper() in ("VARCHAR", "CHAR", "VARCHAR2"): type_str = f"{data_type}({data_length})" elif data_length: type_str = f"{data_type}({data_length})" else: type_str = data_type or "" len_val = str(data_length) if data_length is not None else "-" prec_val = str(data_precision) if data_precision is not None else "-" scale_val = str(data_scale) if data_scale is not None else "-" null_str = "NULL" if nullable == "Y" else "NOT NULL" default_str = (str(data_default).strip() if data_default else "-")[:18] print(fmt.format(col_name, type_str, len_val, prec_val, scale_val, null_str, default_str)) # 查询样本数据(若有) cursor.execute(f'SELECT * FROM "{CONFIG["schema"]}"."{mvi_table}" WHERE ROWNUM <= 3') sample_rows = cursor.fetchall() if sample_rows: col_names = [d[0] for d in cursor.description] print(f"\n 样本数据 (最多 3 行):") for i, row in enumerate(sample_rows, 1): print(f" 行{i}: {dict(zip(col_names, row))}") else: print("\n (表无数据)") except Exception as e: print(f" 查询失败: {e}") # 按 1125暂时属性需求(1).xlsx 查询 METRIC_VALUE_INSTANCE(一条=一个属性) print("\n" + "=" * 80) print("【METRIC_VALUE_INSTANCE】按需求属性查询(xlsx 中一条 = 一个 METRIC_ID)") print("=" * 80) try: # 1. 从 xlsx 解析需求属性列表 req_attrs = _load_required_attrs(XLSX_PATH) print(f"\n xlsx 需求属性 ({len(req_attrs)} 个): {req_attrs}") # 2. 查询库中实际存在的 METRIC_ID(distinct) cursor.execute( f'SELECT DISTINCT METRIC_ID FROM "{CONFIG["schema"]}"."METRIC_VALUE_INSTANCE" ORDER BY METRIC_ID' ) db_metric_ids = [r[0] for r in cursor.fetchall()] print(f"\n 库中已有 METRIC_ID ({len(db_metric_ids)} 个): {db_metric_ids or '(无数据)'}") # 3. METRIC_PROFILE 对照(METRIC_ID -> 中文名) cursor.execute( f'SELECT METRIC_ID, METRIC_NAME FROM "{CONFIG["schema"]}"."METRIC_PROFILE"' ) profile_map = dict(cursor.fetchall()) if profile_map: print("\n METRIC_PROFILE 对照:") for mid, mname in sorted(profile_map.items()): print(f" {mid} -> {mname}") # 4. 交集:需求属性中在库里有的 in_both = [a for a in req_attrs if a in db_metric_ids] # 需求有但库无 missing = [a for a in req_attrs if a not in db_metric_ids] # 库有但需求未列 extra = [m for m in db_metric_ids if m not in req_attrs] print(f"\n 需求与库交集 (可查): {in_both or '无'}") print(f" 需求有但库无: {missing or '无'}") print(f" 库有但需求未列: {extra or '无'}") # 5. 按属性查询示例 SQL print("\n --- 按属性查询示例 SQL ---") for attr in (in_both or req_attrs)[:5]: # 最多展示 5 个 sql = f'SELECT * FROM "{CONFIG["schema"]}"."METRIC_VALUE_INSTANCE" WHERE METRIC_ID = \'{attr}\'' print(f" -- {attr}: {sql}") if len(in_both or req_attrs) > 5: print(f" ... 其余属性同理,将 METRIC_ID = 'xxx' 替换即可") except Exception as e: print(f" 查询失败: {e}") import traceback traceback.print_exc() cursor.execute( """ SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM DBA_TAB_COLUMNS WHERE OWNER = ? ORDER BY TABLE_NAME, COLUMN_ID """, (CONFIG["schema"],), ) rows = cursor.fetchall() print("\n" + "=" * 60) print("所有列详情 (TABLE_NAME | COLUMN_NAME | DATA_TYPE)") print("=" * 60) cur_table = None for table, col, dtype in rows: if table != cur_table: cur_table = table print(f"\n--- {table} ---") print(f" {col}: {dtype}") cursor.close() conn.close() print("\n完成。请根据上述结构在 config 中编写 db.query。") if __name__ == "__main__": main()