sqlglot

sqlglot

SQL解析转译优化和执行引擎

SQLGlot是一个功能全面的SQL工具,支持解析、转译、优化和执行SQL。它可以在21种SQL方言间转换,包括DuckDB、Presto和Spark等。SQLGlot能够处理多种SQL输入,并生成符合目标方言语法和语义的SQL输出。该工具采用纯Python实现,具有出色的性能和完善的测试。SQLGlot还支持自定义解析、查询分析、表达式树遍历和编程式SQL构建等高级功能。

SQLGlotSQL解析器SQL转译器SQL优化器SQL引擎Github开源项目

SQLGlot标志

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

它是一个非常全面的通用SQL解析器,具有强大的测试套件。它也相当高性能,同时完全用Python编写。

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

语法错误会被突出显示,方言不兼容可以根据配置发出警告或引发异常。然而,SQLGlot并不旨在成为一个SQL验证器,因此可能无法检测某些语法错误。

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

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

目录

安装

从PyPI安装:

pip3 install "sqlglot[rs]" # 不带Rust分词器(较慢): # pip3 install sqlglot

或本地checkout:

make install

开发要求(可选):

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"))。

我尝试解析无效的SQL,它成功了,尽管应该引发错误!为什么它没有验证我的SQL?

  • SQLGlot并不旨在成为一个SQL验证器 - 它被设计得非常宽容。这使得代码库更加全面,也为用户提供了更多灵活性,例如允许他们在投影列表中包含尾随逗号。

sqlglot.dataframe发生了什么?

  • PySpark数据框API在第24版中被移到了一个独立的库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: 期望)。第1行,第34列。
  SELECT foo FROM (SELECT baz FROM t
                                   ~

结构化的语法错误可用于程序化使用:

import sqlglot try: sqlglot.transpile("SELECT foo FROM (SELECT baz FROM t") except sqlglot.errors.ParseError as e: print(e.errors)
[{ 'description': '期望)', '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不支持精度 '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不支持精度

有些查询需要额外的信息才能准确转译,比如引用的表的架构。这是因为某些转换是类型敏感的,这意味着需要类型推断来理解它们的语义。尽管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 ```python [ 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  # 完整的测试套件和代码检查

基准测试

基准测试在Python 3.10.12上运行,单位为秒。

查询sqlglotsqlglotrssqlfluffsqltreesqlparsemoz_sql_parsersqloxide
tpch0.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)
short0.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)
long0.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)
crazy0.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)

可选依赖

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

x + interval '1' month

编辑推荐精选

Vora

Vora

免费创建高清无水印Sora视频

Vora是一个免费创建高清无水印Sora视频的AI工具

Refly.AI

Refly.AI

最适合小白的AI自动化工作流平台

无需编码,轻松生成可复用、可变现的AI自动化工作流

酷表ChatExcel

酷表ChatExcel

大模型驱动的Excel数据处理工具

基于大模型交互的表格处理系统,允许用户通过对话方式完成数据整理和可视化分析。系统采用机器学习算法解析用户指令,自动执行排序、公式计算和数据透视等操作,支持多种文件格式导入导出。数据处理响应速度保持在0.8秒以内,支持超过100万行数据的即时分析。

AI工具酷表ChatExcelAI智能客服AI营销产品使用教程
TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
AIWritePaper论文写作

AIWritePaper论文写作

AI论文写作指导平台

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

AI辅助写作AI工具AI论文工具论文写作智能生成大纲数据安全AI助手热门
博思AIPPT

博思AIPPT

AI一键生成PPT,就用博思AIPPT!

博思AIPPT,新一代的AI生成PPT平台,支持智能生成PPT、AI美化PPT、文本&链接生成PPT、导入Word/PDF/Markdown文档生成PPT等,内置海量精美PPT模板,涵盖商务、教育、科技等不同风格,同时针对每个页面提供多种版式,一键自适应切换,完美适配各种办公场景。

AI办公办公工具AI工具博思AIPPTAI生成PPT智能排版海量精品模板AI创作热门
潮际好麦

潮际好麦

AI赋能电商视觉革命,一站式智能商拍平台

潮际好麦深耕服装行业,是国内AI试衣效果最好的软件。使用先进AIGC能力为电商卖家批量提供优质的、低成本的商拍图。合作品牌有Shein、Lazada、安踏、百丽等65个国内外头部品牌,以及国内10万+淘宝、天猫、京东等主流平台的品牌商家,为卖家节省将近85%的出图成本,提升约3倍出图效率,让品牌能够快速上架。

iTerms

iTerms

企业专属的AI法律顾问

iTerms是法大大集团旗下法律子品牌,基于最先进的大语言模型(LLM)、专业的法律知识库和强大的智能体架构,帮助企业扫清合规障碍,筑牢风控防线,成为您企业专属的AI法律顾问。

SimilarWeb流量提升

SimilarWeb流量提升

稳定高效的流量提升解决方案,助力品牌曝光

稳定高效的流量提升解决方案,助力品牌曝光

Sora2视频免费生成

Sora2视频免费生成

最新版Sora2模型免费使用,一键生成无水印视频

最新版Sora2模型免费使用,一键生成无水印视频

下拉加载更多