You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python在SQL查询中重命名表与列(含复杂场景)

解决SQL表/列名替换的表区分与嵌套子查询别名问题

你当前基于sqlglot的实现仅能处理全局名称映射,但在同列名跨表需不同映射、嵌套子查询存在重复别名的场景下会失效。核心问题是没有追踪列所属的表(别名对应的原表),也未处理子查询的独立作用域。


1. 重构映射结构

将原扁平字典改为按表分组的结构,明确每个表对应的列映射规则:

mapping = {
    "tables": {
        "person": "individual",
        "job": "occupation"
    },
    "columns": {
        "person": {
            "name": "full_name",
            "age": "years_old",
            "job_id": "occupation_id"
        },
        "job": {
            "title": "role",
            "job_id": "identifier"
        }
    }
}

2. 完整实现代码

通过封装类来追踪查询的别名作用域,递归处理嵌套子查询,同时结合表别名完成精准的列名替换:

from sqlglot import parse
from sqlglot.expressions import Identifier, Column, Table, Select, Join, SubqueryAlias

class SQLNameTransformer:
    def __init__(self, mapping):
        self.table_mapping = mapping.get("tables", {})
        self.column_mapping = mapping.get("columns", {})
    
    def _collect_alias_mapping(self, node, alias_map):
        """递归收集当前查询中别名到原表名的映射,同时替换表名"""
        # 处理基础表
        if isinstance(node, Table):
            alias = node.args.get("alias")
            if alias:
                alias_name = alias.args.get("this")
                original_table = node.args.get("this")
                if alias_name and original_table:
                    alias_map[alias_name] = original_table
            # 替换表名
            original_table = node.args.get("this")
            if original_table in self.table_mapping:
                node.args["this"] = self.table_mapping[original_table]
            return
        
        # 处理JOIN子句
        if isinstance(node, Join):
            self._collect_alias_mapping(node.args.get("this"), alias_map)
            return
        
        # 处理子查询别名(如 (SELECT ...) AS t)
        if isinstance(node, SubqueryAlias):
            alias = node.args.get("alias")
            if alias:
                alias_name = alias.args.get("this")
                alias_map[alias_name] = alias_name
            # 递归处理子查询内部,使用独立的别名映射
            subquery = node.args.get("this")
            if isinstance(subquery, Select):
                self._collect_alias_mapping(subquery, {})
            return
        
        # 处理SELECT语句,遍历FROM/JOIN等子句
        if isinstance(node, Select):
            from_expr = node.args.get("from")
            if from_expr:
                self._collect_alias_mapping(from_expr, alias_map)
            for join in node.args.get("joins", []):
                self._collect_alias_mapping(join, alias_map)
            # 处理WHERE/HAVING中的子查询
            for key in ["where", "having"]:
                expr = node.args.get(key)
                if expr:
                    self._collect_alias_mapping(expr, {})
            return
        
        # 递归处理所有子节点
        for key, value in node.args.items():
            if isinstance(value, list):
                for item in value:
                    self._collect_alias_mapping(item, alias_map)
            elif hasattr(value, "args"):
                self._collect_alias_mapping(value, alias_map)
    
    def _transform_column(self, node, alias_map):
        """结合别名映射,替换列名"""
        prefix = node.args.get("this").args.get("prefix")
        column_name = node.args.get("this").args.get("this")
        
        if not prefix or not column_name:
            return node
        
        # 通过别名找到原表,再查对应列的映射
        original_table = alias_map.get(prefix)
        if not original_table:
            return node
        
        table_columns = self.column_mapping.get(original_table, {})
        if column_name in table_columns:
            node.args["this"].args["this"] = table_columns[column_name]
        return node
    
    def transform(self, sql):
        expressions = parse(sql)
        transformed_exprs = []
        
        for expr in expressions:
            alias_map = {}
            self._collect_alias_mapping(expr, alias_map)
            # 遍历替换所有列名
            expr = expr.transform(lambda node: self._transform_column(node, alias_map) if isinstance(node, Column) else node)
            transformed_exprs.append(expr)
        
        return "".join(e.sql() for e in transformed_exprs)

# 测试用例
if __name__ == "__main__":
    sql = '''SELECT p.name, p.age, j.title, p.job_id, j.job_id
FROM person AS p
LEFT JOIN job AS j ON p.job_id = j.job_id
WHERE p.age > 30
AND EXISTS (SELECT 1 FROM person AS p WHERE p.age < 18)'''
    
    transformer = SQLNameTransformer(mapping)
    print(transformer.transform(sql))

3. 代码说明

  • _collect_alias_mapping:递归遍历AST,收集当前查询(含子查询)的别名-原表映射,同时直接完成表名替换。子查询会使用独立的别名映射,避免和外部查询冲突。
  • _transform_column:根据列的前缀(别名)找到所属原表,再匹配对应的列映射规则完成替换。
  • 嵌套子查询的作用域被独立处理,重复别名不会互相干扰。

测试输出的SQL为:

SELECT p.full_name, p.years_old, j.role, p.occupation_id, j.identifier FROM individual AS p LEFT JOIN occupation AS j ON p.occupation_id = j.identifier WHERE p.years_old > 30 AND EXISTS (SELECT 1 FROM individual AS p WHERE p.years_old < 18)

内容的提问来源于stack exchange,提问作者Heisen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 17:17:49