
用 SQLGlot 打通多数据库SQL 解析器 3 大核心能力与跨库迁移实战指南【免费下载链接】sqlglotPython SQL Parser and Transpiler项目地址: https://gitcode.com/gh_mirrors/sq/sqlglot想象一下这样的场景老板把一份写着 MySQL 语法的查询丢给你要求立刻在 BigQuery 上跑出结果。你把DATE_FORMAT改成FORMAT_TIMESTAMP把IFNULL换成IFNULL……改了十处跑起来又报三个错。跨数据库迁移的痛苦经历过的人都懂。而今天要介绍的SQLGlot就是一个能让你从这种痛苦中解脱出来的 Python 工具——它把 33 种 SQL 方言的互相转换、语法检查、查询优化统统打包成了一个零依赖的库你只需要两行代码就能完成过去需要半天手改的工作。从翻译官说起SQLGlot 到底是什么你可以把 SQLGlot 想象成一位精通 33 种数据库方言的随身翻译官。你给它一句用方言 A写的 SQL它先读懂这句话想表达什么而不是死记硬背字符串再用方言 B重新说一遍。关键就在先读懂这一步SQLGlot 会把 SQL 解析成一棵抽象语法树AST——一种用节点表示选择了哪几列、从哪个表、怎么连接、怎么过滤的树状结构。一旦 SQL 变成了结构化的树后续的转译、格式化、优化、血缘分析就都变成了操作这棵树而不是处理字符串。这也解释了为什么它叫 SQLGlot——Glot 取自 polyglot通晓多国语言的人。3 分钟快速上手安装与最小示例SQLGlot 是纯 Python 实现、零第三方依赖安装非常简单pip3 install sqlglot装好后先跑一个最小示例确认环境没问题import sqlglot # 从 DuckDB 方言转译到 Hive 方言 print(sqlglot.transpile(SELECT EPOCH_MS(1618088028295), readduckdb, writehive)) # 输出[SELECT FROM_UNIXTIME(1618088028295 / POW(10, 3))]这段代码解决的是同一个查询在不同数据库上跑的问题EPOCH_MS是 DuckDB 的函数SQLGlot 自动把它翻译成了 Hive 认识的FROM_UNIXTIME连时间精度换算除以 10 的 3 次方都帮你处理好了。核心能力一多方言 SQL 转译写一次到处跑转译是 SQLGlot 最出圈的能力。官方支持 33 种方言DuckDB、Presto/Trino、Spark/Databricks、Snowflake、BigQuery、MySQL、PostgreSQL、ClickHouse……基本覆盖了主流数仓和 OLTP 数据库。转译的核心是transpile函数它接收一段 SQL、指定读入方言和写出方言返回一个列表因为一段脚本可能包含多条语句import sqlglot # 从 MySQL 转换到 PostgreSQL result sqlglot.transpile( SELECT DATE_FORMAT(created_at, %Y-%m-%d) FROM users, readmysql, writepostgres, ) print(result) # 输出[SELECT TO_CHAR(CAST(created_at AS TIMESTAMP), YYYY-MM-DD) FROM users]注意看SQLGlot 不只是替换函数名它把 MySQL 的DATE_FORMAT完整重构成了 PostgreSQL 的TO_CHAR还自动补上了CAST类型转换。再比如 Snowflake 的DATE_TRUNC(month, created_at)转成 BigQuery 就是TIMESTAMP_TRUNC(created_at, MONTH)参数顺序都帮你调整好了。实际价值多数据源汇聚分析、数仓上云迁移、BI 工具 SQL 适配——这类写一次、处处跑的需求用transpile写一个循环就能批量处理queries [ SELECT DATE_FORMAT(created_at, %Y-%m-%d) FROM users, SELECT EPOCH_MS(1618088028295), ] for sql in queries: print(sqlglot.transpile(sql, readmysql, writebigquery))核心能力二操作抽象语法树像摆弄字典一样摆弄 SQL如果说转译是开箱即用那 AST 操作就是 SQLGlot 真正强大的地方。解析得到的语法树是一个普通 Python 对象你可以遍历它、搜索它、修改它。用parse_one把 SQL 变成 AST然后遍历找出所有列import sqlglot from sqlglot import exp ast sqlglot.parse_one(SELECT a FROM (SELECT a FROM t) AS x) # 遍历 AST 的所有节点 for node in ast.walk(): if isinstance(node, exp.Column): print(f找到列: {node.name})这段代码解决的问题是我怎么从一段 SQL 里提取出所有引用的列——这正是做 SQL 静态分析、数据字典盘点、权限控制的基础。除了遍历你还能直接生成美化后的 SQL。工作中常见的一段压成一行、完全没有缩进的 SQL用一行代码就能格式化from sqlglot import parse_one ugly_sql SELECT * FROM users WHERE age18 ORDER BY created_at DESC print(parse_one(ugly_sql).sql(prettyTrue, identifyTrue))prettyTrue负责换行缩进identifyTrue会给标识符加上双引号输出立刻变成可读性很高的标准格式。想进一步了解 AST 的节点类型和遍历方法可以看仓库里的入门文档 posts/ast_primer.md。核心能力三内置优化器自动重写查询提升性能SQLGlot 不只是看懂SQL它还能改进SQL。optimizer.optimize会执行一系列规则去掉冗余子查询、合并可合并的 JOIN、把过滤条件下推、自动限定列名、消除未使用的列等。下面这段代码把一条手写的查询交给优化器处理from sqlglot import parse_one from sqlglot.optimizer import optimize sql SELECT users.name, orders.total FROM users JOIN orders ON users.id orders.user_id WHERE orders.created_at 2024-01-01 GROUP BY users.name optimized optimize(parse_one(sql)) print(optimized.sql(prettyTrue))优化后的结果会看到两个明显变化一是所有列名都被自动加上了表名前缀users.name、orders.user_id消除了歧义二是WHERE里的过滤条件orders.created_at ...被下推到了 JOIN 的ON子句中让数据库能更早地过滤数据、减少 JOIN 的中间结果。实际价值在把查询分发给底层数据库执行之前先过一遍优化器等于给你的查询做了一次免费预编译优化尤其适合数据平台类产品对用户提交的 SQL 做预处理。实战演练数据血缘分析与 SQL 差异检查前面学的转译、AST、优化已经足够应付日常但 SQLGlot 还有两个杀器数据血缘分析和SQL 差异比较它们在数据治理和 CI/CD 场景里非常有用。追踪列的血缘一个查询读懂数据流向sqlglot.lineage.lineage能追踪某个列从源头表到最终结果的完整传递路径。下面这段代码把一条含 CTE 的查询中traced_col列的来龙去脉查出来from sqlglot.lineage import lineage result lineage( traced_col, WITH cte AS (SELECT traced_col FROM intermediate) SELECT traced_col FROM cte, dialectduckdb, ) print(result)它会告诉你这个列是从哪个根表、经过哪些中间层一路流动过来的。下图展示了典型的列血缘链路数据从底部的root_table出发经过中间表最终到达顶部的 CTE 输出。比较两个 SQL精准定位逻辑差异在代码评审或 SQL 版本迭代时你常常想知道这条 SQL 改了什么。SQLGlot 的diff模块通过对比两棵 AST 的结构差异来实现from sqlglot import diff, parse_one changes diff( parse_one(SELECT a, b FROM t), parse_one(SELECT a, c FROM t), ) print(changes) # 输出包含 Remove(列 b) 和 Insert(列 c) 等变更记录它不依赖字符串比对而是基于语法树节点的语义映射——所以哪怕只是换了个别名、调整了括号都能正确识别其实没变。实战价值把血缘分析接进数据质量平台、把 diff 接进 CI 流程就能自动生成本次发布改了哪些表的哪些列的变更清单这是手工 Review 很难做到的。避坑指南新手最容易踩的 5 个坑import sqlglot from sqlglot import parse_one from sqlglot.optimizer.qualify import qualify # 坑1read / write 写反了 # 结果会变成用目标方言读、用源方言写报错或乱输出 sqlglot.transpile(SELECT 1, readmysql, writebigquery) # 坑2忽略转译返回的是列表 # transpile 返回 list直接当字符串用会报错 result sqlglot.transpile(SELECT 1, readmysql, writebigquery)[0]常见问题表现解决办法read / write 参数颠倒语法报错或输出混乱记住read 读入方言、write 写出方言忘了[0]取列表元素拿到 list 而非字符串transpile(...)[0]未捕获 ParseError括号不平衡等错误直接抛异常用try/except sqlglot.errors.ParseError包住优化后列名带前缀变样查询结果似乎多了别名这是 qualify 的预期行为可关闭qualify相关规则语法不合法但没报错转译结果为空或奇怪先parse_one检查能否解析错误处理的推荐写法try: sqlglot.transpile(SELECT foo FROM (SELECT baz FROM t) except sqlglot.errors.ParseError as e: print(fSQL 语法错误: {e})总结与下一步回顾一下本文的核心内容多方言转译transpile一行代码在 33 种方言间自由转换函数、类型、参数顺序都帮你调整AST 操作parse_one把 SQL 变成可遍历、可修改、可美化的树结构是静态分析的基石查询优化optimize自动下推条件、限定列名、去冗余免费给你的查询做预编译优化进阶能力lineage做列级血缘分析、diff做 AST 级差异比较避坑要点read/write 方向、返回值类型、异常捕获是新手最常踩的坑SQLGlot 适合这几类人要做跨库迁移或 SQL 适配的数据工程师、想构建 SQL 静态分析工具的平台开发者、需要对查询做自动改写优化的后端工程师以及想深入理解 SQL 解析原理的学习者。想继续深入可以从这几处入手转译实现看sqlglot/generators/目录解析规则看sqlglot/parsers/目录优化器规则在sqlglot/optimizer/目录每个模块都有清晰的边界和注释非常适合当源码教材来读。现在打开你的终端pip3 install sqlglot把你手头那条最折磨人的跨库查询丢给transpile试试——你会发现那些曾经让人通宵的语法差异其实一行代码就能解决。【免费下载链接】sqlglotPython SQL Parser and Transpiler项目地址: https://gitcode.com/gh_mirrors/sq/sqlglot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考