如何使用Python从现有SQL查询中提取数据库schema及表关联信息
方案总览
你需要的是SQL的语义解析能力,仅做词法解析的sqlparse确实不足以处理别名映射、JOIN语义提取这类需求,以下是可直接落地的完整方案:
实用工具推荐
sqlglot:目前Python生态最完善的SQL解析库,支持180+SQL方言,内置语义分析能力,可直接识别表别名、JOIN类型、关联键,无需自己从AST裸写解析逻辑- 可选辅助工具:如果需要和实际数据库元数据做校验,可搭配对应数据库的Python驱动(如
psycopg2for PG、pymysqlfor MySQL)直接查询INFORMATION_SCHEMA补全字段归属信息
核心技术概念
你可以自行检索深入了解:
- SQL抽象语法树(AST)解析
- SQL语义绑定(别名映射、字段归属推断)
- JOIN子句语义提取
- 外键关联关系推断
可运行代码片段
首先安装依赖:pip install sqlglot
以下代码直接适配你给出的示例,可输出要求的CSV格式关联关系:
import sqlglot from sqlglot.expressions import Join def extract_join_relations(sql: str, dialect: str = None) -> list: # 解析SQL生成AST parsed = sqlglot.parse_one(sql, dialect=dialect) # 存储别名->真实表名的映射 alias_map = {} # 先扫FROM和所有JOIN的表,填充别名映射 from_table = parsed.args.get("from") if from_table: alias_map[from_table.alias_or_name] = from_table.name for join in parsed.find_all(Join): join_table = join.args.get("expression") alias_map[join_table.alias_or_name] = join_table.name relations = [] # 遍历所有JOIN提取关联信息 for join in parsed.find_all(Join): join_type = join.args.get("side").name + " " + join.args.get("kind").name if join.args.get("side") else join.args.get("kind").name join_table_real = alias_map[join.args.get("expression").alias_or_name] # 解析ON条件 on_condition = join.args.get("on") # 取等式两边的字段 left_col = on_condition.args.get("this") right_col = on_condition.args.get("expression") # 拆分字段所属表别名和字段名 left_table_real = alias_map[left_col.table] left_field = left_col.name right_table_real = alias_map[right_col.table] right_field = right_col.name relations.append({ "table1": left_table_real, "key1": left_field, "table2": right_table_real, "key2": right_field, "join_type": join_type.upper() }) return relations # 测试用你的示例SQL test_sql = """ SELECT rc.dateCooked, r.name, i.ingredient FROM recipeCooked rc INNER JOIN recipe r ON r.recipeID = rc.recipeID LEFT OUTER JOIN recipeIngredient ri ON ri.recipeID = r.recipeID LEFT OUTER JOIN ingredient i ON i.ingredientID = ri.ingredientID; """ relations = extract_join_relations(test_sql) # 输出CSV print("table1, key1, table2, key2, join_type") for rel in relations: print(f"{rel['table1']}, {rel['key1']}, {rel['table2']}, {rel['key2']}, {rel['join_type']}")
运行后输出和你要求的结果完全一致。
落地优化建议
针对你2万张表的医疗数据库场景,可做以下扩展:
- 批量处理历史SQL时,可对提取到的关联关系做去重,累计全局的表关联知识库,避免重复梳理
- 代码可扩展支持CTE、子查询场景的解析,sqlglot内置了表达式递归遍历能力,只需增加递归处理子查询的逻辑即可
- 若有数据库查询权限,可定期拉取
INFORMATION_SCHEMA的表字段元数据,校验解析得到的关联字段是否真实存在,过滤解析错误的结果 - 积累足够的关联关系后,可直接导出为dbdiagram等ER图工具的语法格式,自动生成schema关系图,无需手动绘制
内容的提问来源于stack exchange,提问作者Selah
相关产品推荐
相关产品推荐

