首页
学习
活动
专区
圈层
工具
发布
MCP广场 >详情页
SQLGlot解析器2026-06-12467分享添加福利群:解决AI开发者的「MCP实战痛点」
SQLGlot 是一个功能强大的无依赖 SQL 解析器、转换器、优化器和执行引擎,完全用 Python 编写。它支持在 27 种不同的 SQL 方言之间进行转换,如 DuckDB、Presto、Spark、Snowflake 和 BigQuery 等。SQLGlot 的核心功能包括 SQL 格式化、语法错误检测、元数据分析、表达式树遍历、SQL 构建与修改、查询优化以及 SQL 执行。它还允许用户自定义方言,并提供了高性能的解析和转换能力。SQLGlot 适用于 SQL 查询的标准化、优化和跨平台兼容性处理,广泛应用于数据工程和数据分析领域。
By tobymao
2026-06-12467
github
详情内容

SQLGlot 标志

SQLGlot 是一个无依赖的 SQL 解析器、转译器、优化器和执行引擎。它可用于格式化 SQL 或在 31 种不同方言 之间进行转换,如 DuckDBPresto / TrinoSpark / DatabricksSnowflakeBigQuery。它旨在读取各种 SQL 输入,并以目标方言输出语法和语义正确的 SQL。

它是一个非常全面的通用 SQL 解析器,具有健壮的 测试套件。虽然完全用 Python 编写,但它的 性能 也相当出色。

您可以轻松地 自定义 解析器、分析 查询、遍历表达式树并以编程方式 构建 SQL。

SQLGlot 可以检测多种 语法错误,例如不匹配的括号、保留关键字的不正确使用等。这些错误会被高亮显示,并且根据配置,方言不兼容性可以警告或引发错误。

在 API 文档 和表达式树 入门指南 中了解更多关于 SQLGlot 的信息。

SQLGlot 非常欢迎贡献;阅读 贡献指南入门文档 开始吧!

目录

安装

从 PyPI 安装:

pip3 install "sqlglot[rs]"

# 不使用 Rust 分词器(较慢):
# pip3 install sqlglot

或从本地检出安装:

# 可选前缀 UV=1 以使用 uv 进行安装
make install

开发需求(可选):

# 可选前缀 UV=1 以使用 uv 进行安装
make install-dev

版本控制

给定版本号 MAJOR.MINOR.PATCH,SQLGlot 使用以下版本控制策略:

  • PATCH 版本在有向后兼容的修复或功能添加时递增。
  • MINOR 版本在有向后不兼容的修复或功能添加时递增。
  • MAJOR 版本在有重大向后不兼容的修复或功能添加时递增。

联系我们

我们很乐意听取您的意见。加入我们的社区 Slack 频道!

常见问题

我尝试解析应该是有效的 SQL,但失败了,为什么会这样?

  • 大多数情况下,这类问题是因为在解析时省略了“源”方言。例如,正确解析用 Spark SQL 编写的 SQL 查询的方法是:parse_one(sql, dialect="spark")(或者:read="spark")。如果未指定方言,parse_one 将尝试根据“SQLGlot 方言”解析查询,该方言设计为所有支持方言的超集。如果您已指定方言但仍无法工作,请提交问题。

我尝试输出 SQL,但它不是正确的方言!

  • 与解析类似,生成 SQL 也需要指定目标方言,否则默认使用 SQLGlot 方言。例如,要将 Spark SQL 查询转译为 DuckDB,执行 parse_one(sql, dialect="spark").sql(dialect="duckdb")(或者:transpile(sql, read="spark", write="duckdb"))。

sqlglot.dataframe 发生了什么?

  • PySpark 数据帧 API 在 v24 中被移到了一个名为 SQLFrame 的独立库中。现在它允许您运行查询,而不仅仅是生成 SQL。

示例

格式化和转译

轻松在方言之间转换。例如,日期/时间函数因方言而异,可能难以处理:

import sqlglot
sqlglot.transpile("SELECT EPOCH_MS(1618088028295)", read="duckdb", write="hive")[0]
'SELECT FROM_UNIXTIME(1618088028295 / POW(10, 3))'

SQLGlot 甚至可以转换自定义时间格式:

import sqlglot
sqlglot.transpile("SELECT STRFTIME(x, '%y-%-m-%S')", read="duckdb", write="hive")[0]
"SELECT DATE_FORMAT(x, 'yy-M-ss')"

标识符分隔符和数据类型也可以转换:

import sqlglot

# Spark SQL 要求用反引号 (`) 分隔标识符,并使用 `FLOAT` 而不是 `REAL`
sql = """WITH baz AS (SELECT a, c FROM foo WHERE a = 1) SELECT f.a, b.b, baz.c, CAST("b"."a" AS REAL) d FROM foo f JOIN bar b ON f.a = b.a LEFT JOIN baz ON f.a = baz.a"""

# 将查询转换为 Spark SQL,格式化并分隔所有标识符
print(sqlglot.transpile(sql, write="spark", identify=True, pretty=True)[0])
WITH `baz` AS (
  SELECT
    `a`,
    `c`
  FROM `foo`
  WHERE
    `a` = 1
)
SELECT
  `f`.`a`,
  `b`.`b`,
  `baz`.`c`,
  CAST(`b`.`a` AS FLOAT) AS `d`
FROM `foo` AS `f`
JOIN `bar` AS `b`
  ON `f`.`a` = `b`.`a`
LEFT JOIN `baz`
  ON `f`.`a` = `baz`.`a`

注释也会尽可能保留:

sql = """
/* 多
   行
   注释
*/
SELECT
  tbl.cola /* 注释 1 */ + tbl.colb /* 注释 2 */,
  CAST(x AS SIGNED), # 注释 3
  y               -- 注释 4
FROM
  bar /* 注释 5 */,
  tbl #          注释 6
"""

# 注意:MySQL 特有的注释 (`#`) 被转换为标准语法
print(sqlglot.transpile(sql, read='mysql', pretty=True)[0])
/* 多
   行
   注释
*/
SELECT
  tbl.cola /* 注释 1 */ + tbl.colb /* 注释 2 */,
  CAST(x AS INT), /* 注释 3 */
  y /* 注释 4 */
FROM bar /* 注释 5 */, tbl /*          注释 6 */

元数据

您可以使用表达式助手探索 SQL,例如查找查询中的列和表:

from sqlglot import parse_one, exp

# 打印所有列引用(a 和 b)
for column in parse_one("SELECT a, b + 1 AS c FROM d").find_all(exp.Column):
    print(column.alias_or_name)

# 查找所有 SELECT 语句中的投影(a 和 c)
for select in parse_one("SELECT a, b + 1 AS c FROM d").find_all(exp.Select):
    for projection in select.expressions:
        print(projection.alias_or_name)

# 查找所有表(x, y, z)
for table in parse_one("SELECT * FROM x JOIN y JOIN z").find_all(exp.Table):
    print(table.name)

阅读 AST 入门指南 以了解更多关于 SQLGlot 内部的信息。

解析器错误

当解析器检测到语法错误时,会引发 ParseError

import sqlglot
sqlglot.transpile("SELECT foo FROM (SELECT baz FROM t")
sqlglot.errors.ParseError: Expecting ). Line 1, Col: 34.
  SELECT foo FROM (SELECT baz FROM t
                                   ~

结构化语法错误可供编程使用:

import sqlglot.errors
try:
    sqlglot.transpile("SELECT foo FROM (SELECT baz FROM t")
except sqlglot.errors.ParseError as e:
    print(e.errors)
[{
  'description': 'Expecting )',
  'line': 1,
  'col': 34,
  'start_context': 'SELECT foo FROM (SELECT baz FROM ',
  'highlight': 't',
  'end_context': '',
  'into_expression': None
}]

不支持的错误

某些查询可能无法在某些方言之间转换。对于这些情况,SQLGlot 可能会发出警告,并默认进行尽力而为的转换:

import sqlglot
sqlglot.transpile("SELECT APPROX_DISTINCT(a, 0.1) FROM foo", read="presto", write="hive")
APPROX_COUNT_DISTINCT does not support accuracy
'SELECT APPROX_COUNT_DISTINCT(a) FROM foo'

可以通过设置 unsupported_level 属性来更改此行为。例如,我们可以将其设置为 RAISEIMMEDIATE 以确保引发异常:

import sqlglot
sqlglot.transpile("SELECT APPROX_DISTINCT(a, 0.1) FROM foo", read="presto", write="hive", unsupported_level=sqlglot.ErrorLevel.RAISE)
sqlglot.errors.UnsupportedError: APPROX_COUNT_DISTINCT does not support accuracy

有些查询需要额外的信息才能准确转译,例如它们引用的表的模式。这是因为某些转换是类型敏感的,意味着需要类型推断才能理解其语义。尽管 qualifyannotate_types 优化器 规则 可以帮助解决这个问题,但它们默认不使用,因为它们增加了显著的开销和复杂性。

转译通常是一个难题,因此 SQLGlot 采用“增量”方法来解决它。这意味着目前可能有些方言对缺少某些输入的支持,但预计随着时间的推移会有所改进。我们非常欢迎有详细文档和测试的问题或 PR,因此如果您需要指导,请随时 联系我们

SQL构建与修改

SQLGlot支持增量式构建SQL表达式:

from sqlglot import select, condition

where = condition("x=1").and_("y=1")
select("*").from_("y").where(where).sql()
'SELECT * FROM y WHERE x = 1 AND y = 1'

可以修改已解析的语法树:

from sqlglot import parse_one
parse_one("SELECT x FROM y").from_("z").sql()
'SELECT x FROM z'

通过向语法树中的每个节点应用映射函数,还可以递归转换已解析的表达式:

from sqlglot import exp, parse_one

expression_tree = parse_one("SELECT a FROM x")

def transformer(node):
    if isinstance(node, exp.Column) and node.name == "a":
        return parse_one("FUN(a)")
    return node

transformed_tree = expression_tree.transform(transformer)
transformed_tree.sql()
'SELECT FUN(a) FROM x'

SQL优化器

SQLGlot能将查询重写为"优化"形式。它采用多种技术创建新的规范AST。该AST可用于标准化查询或为实际引擎实现奠定基础。例如:

import sqlglot
from sqlglot.optimizer import optimize

print(
    optimize(
        sqlglot.parse_one("""
            SELECT A OR (B OR (C AND D))
            FROM x
            WHERE Z = date '2021-01-01' + INTERVAL '1' month OR 1 = 0
        """),
        schema={"x": {"A": "INT", "B": "INT", "C": "INT", "D": "INT", "Z": "STRING"}}
    ).sql(pretty=True)
)
SELECT
  (
    "x"."a" <> 0 OR "x"."b" <> 0 OR "x"."c" <> 0
  )
  AND (
    "x"."a" <> 0 OR "x"."b" <> 0 OR "x"."d" <> 0
  ) AS "_col_0"
FROM "x" AS "x"
WHERE
  CAST("x"."z" AS DATE) = CAST('2021-02-01' AS DATE)

AST内省

通过调用repr可以查看解析后SQL的AST版本:

from sqlglot import parse_one
print(repr(parse_one("SELECT a + 1 AS z")))
Select(
  expressions=[
    Alias(
      this=Add(
        this=Column(
          this=Identifier(this=a, quoted=False)),
        expression=Literal(this=1, is_string=False)),
      alias=Identifier(this=z, quoted=False))])

AST差异比较

SQLGlot能计算两个表达式间的语义差异,并以需要执行的操作序列形式输出变更,这些操作用于将源表达式转换为目标表达式:

from sqlglot import diff, parse_one
diff(parse_one("SELECT a + b, c, d"), parse_one("SELECT c, a - b, d"))
[
  Remove(expression=Add(
    this=Column(
      this=Identifier(this=a, quoted=False)),
    expression=Column(
      this=Identifier(this=b, quoted=False)))),
  Insert(expression=Sub(
    this=Column(
      this=Identifier(this=a, quoted=False)),
    expression=Column(
      this=Identifier(this=b, quoted=False)))),
  Keep(
    source=Column(this=Identifier(this=a, quoted=False)),
    target=Column(this=Identifier(this=a, quoted=False))),
  ...
]

另见:SQL语义差异比较

自定义方言

可通过继承Dialect类来添加方言

from sqlglot import exp
from sqlglot.dialects.dialect import Dialect
from sqlglot.generator import Generator
from sqlglot.tokens import Tokenizer, TokenType


class Custom(Dialect):
    class Tokenizer(Tokenizer):
        QUOTES = ["'", '"']
        IDENTIFIERS = ["`"]

        KEYWORDS = {
            **Tokenizer.KEYWORDS,
            "INT64": TokenType.BIGINT,
            "FLOAT64": TokenType.DOUBLE,
        }

    class Generator(Generator):
        TRANSFORMS = {exp.Array: lambda self, e: f"[{self.expressions(e)}]"}

        TYPE_MAPPING = {
            exp.DataType.Type.TINYINT: "INT64",
            exp.DataType.Type.SMALLINT: "INT64",
            exp.DataType.Type.INT: "INT64",
            exp.DataType.Type.BIGINT: "INT64",
            exp.DataType.Type.DECIMAL: "NUMERIC",
            exp.DataType.Type.FLOAT: "FLOAT64",
            exp.DataType.Type.DOUBLE: "FLOAT64",
            exp.DataType.Type.BOOLEAN: "BOOL",
            exp.DataType.Type.TEXT: "STRING",
        }

print(Dialect["custom"])
<class '__main__.Custom'>

SQL执行

SQLGlot能解释执行SQL查询,其中表用Python字典表示。该引擎不追求速度,但可用于单元测试和在Python对象间原生运行SQL。此外,该基础可轻松集成快速计算内核,如ArrowPandas

以下示例展示了一个涉及聚合和连接的查询执行:

from sqlglot.executor import execute

tables = {
    "sushi": [
        {"id": 1, "price": 1.0},
        {"id": 2, "price": 2.0},
        {"id": 3, "price": 3.0},
    ],
    "order_items": [
        {"sushi_id": 1, "order_id": 1},
        {"sushi_id": 1, "order_id": 1},
        {"sushi_id": 2, "order_id": 1},
        {"sushi_id": 3, "order_id": 2},
    ],
    "orders": [
        {"id": 1, "user_id": 1},
        {"id": 2, "user_id": 2},
    ],
}

execute(
    """
    SELECT
      o.user_id,
      SUM(s.price) AS price
    FROM orders o
    JOIN order_items i
      ON o.id = i.order_id
    JOIN sushi s
      ON i.sushi_id = s.id
    GROUP BY o.user_id
    """,
    tables=tables
)
user_id price
      1   4.0
      2   3.0

另见:从零编写Python SQL引擎

使用者

文档

SQLGlot使用pdoc提供API文档。

托管版本在SQLGlot网站上,也可本地构建:

make docs-serve

运行测试与代码检查

make style  # 仅代码检查
make unit   # 仅单元测试(或unit-rs使用Rust分词器)
make test   # 单元和集成测试(或test-rs使用Rust分词器)
make check  # 完整测试套件和代码检查

部署

部署新版本SQLGlot的步骤:

  1. 运行git pull确保本地git仓库在main分支头部
  2. 如果Rust分词器代码自上次发布后有变更:
    1. sqlglotrs/Cargo.tomlpackage头下增加version属性
    2. 运行make install-dev更新Cargo.lock文件
    3. 提交对Cargo.tomlCargo.lock的更改
  3. 执行git tag操作增加版本号,如git tag v28.5.0
  4. 运行git push && git push --tags部署新版本

[!重要] 如果自上次发布后有破坏性变更,请确保为sqlglot和sqlglotrs发布次要或主要版本。参考SQLGlot的版本控制方案获取更多信息。

基准测试

基准测试在Python 3.10.12环境下运行(单位:秒)。

查询 sqlglot sqlglotrs sqlfluff sqltree sqlparse moz_sql_parser sqloxide
tpch 0.00944 (1.0) 0.00590 (0.625) 0.32116 (33.98) 0.00693 (0.734) 0.02858 (3.025) 0.03337 (3.532) 0.00073 (0.077)
short 0.00065 (1.0) 0.00044 (0.687) 0.03511 (53.82) 0.00049 (0.759) 0.00163 (2.506) 0.00234 (3.601) 0.00005 (0.073)
long 0.00889 (1.0) 0.00572 (0.643) 0.36982 (41.56) 0.00614 (0.690) 0.02530 (2.844) 0.02931 (3.294) 0.00059 (0.066)
crazy 0.02918 (1.0) 0.01991 (0.682) 1.88695 (64.66) 0.02003 (0.686) 7.46894 (255.9) 0.64994 (22.27) 0.00327 (0.112)
make bench            # 运行解析基准测试
make bench-optimize   # 运行优化基准测试

可选依赖

SQLGlot使用dateutil简化字面时间间隔表达式。如果找不到该模块,优化器将不会简化如下表达式:

x + interval '1' month

支持的方言

方言 支持级别
Athena 官方支持
BigQuery 官方支持
ClickHouse 官方支持
Databricks 官方支持
Doris 社区支持
Dremio 社区支持
Drill 社区支持
Druid 社区支持
DuckDB 官方支持
Exasol 社区支持
Fabric 社区支持
Hive 官方支持
Materialize 社区支持
MySQL 官方支持
Oracle 官方支持
Postgres 官方支持
Presto 官方支持
PRQL 社区支持
Redshift 官方支持
RisingWave 社区支持
SingleStore 社区支持
Snowflake 官方支持
Solr 社区支持
Spark 官方支持
SQLite 官方支持
StarRocks 官方支持
Tableau 官方支持
Teradata 社区支持
Trino 官方支持
TSQL 官方支持
YDB 插件

官方方言由核心SQLGlot团队维护,享有更高的错误修复和功能添加优先级。

社区方言主要由社区贡献者开发和维护。这些方言功能完整,但问题解决优先级可能低于官方支持方言。我们欢迎并鼓励社区贡献以改进这些方言。

插件方言(自v28.6.0起支持)是由独立贡献者在外部代码库中开发和维护的第三方方言。这些方言不属于SQLGlot代码库的一部分,而是作为独立的软件包分发。SQLGlot团队不为插件方言提供支持或维护——如有任何问题或功能需求,请直接联系其对应的代码库。关于如何构建自己的方言插件,请参阅下文创建方言插件部分。

创建方言插件

如果您的数据库不受支持,您可以创建一个插件,通过入口点注册自定义方言。创建一个包含您的方言类的软件包,并在setup.py中进行注册:

from setuptools import setup

setup(
    name="mydb-sqlglot-dialect",
    entry_points={
        "sqlglot.dialects": [
            "mydb = my_package.dialect:MyDB",
        ],
    },
)

该方言将被自动发现,并可以像任何内置方言一样使用:

from sqlglot import transpile
transpile("SELECT * FROM t", read="mydb", write="postgres")

有关实现细节,请参阅自定义方言部分。

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档