Files
module1_3/final/scripts/inspect_dm_tables.py
2026-04-23 12:48:59 +08:00

242 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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_IDdistinct
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()