Skip to content

sqlparse

sqlparse用于拆分、解析和格式化 SQL 语句的 Python 库。

公开接口

他提供了三个公开的接口来实现拆分、解析和格式化:

  • sqlparse.split(sql: str, encoding=None) -> list[str]: 将多条 SQL 语句拆分为单条语句
  • sqlparse.format(sql:str, encoding=None, **options) -> str: 根据选项格式化 SQL
  • sqlparse.parse(sql:str, encoding=None) -> tuple(Statement): 解析 SQL 并返回一个元组其中包含表示语句的 Statement 对象

split

该方法是最简单的:

Python
sql = "SELECT * FROM users; INSERT INTO users (id, name) VALUES (1, 'Alice');"
statements = sqlparse.split(sql)
for stmt in statements:
    print(stmt)

# SELECT * FROM users;
# INSERT INTO users (id, name) VALUES (1, 'Alice');

format

他用于格式化 SQL 语句,他接受以下参数:

  • keyword_case: "upper" | "lower" | "capitalize": 关键字格式,默认就是关键字全部大写
  • identifier_case: "upper" | "lower" | "capitalize": 标识符格式
  • strip_comments: bool = False: 如果为 True 这删除注释
  • truncate_strings: number|None = None: 如果是正整数,长度超过给定值的字符串被截断
  • truncate_char: str = "[...]": 被截断的字符串后附加的字符串
  • reindent_aligned: bool = False: 如果为 True 将缩进语句并通过关键字对齐
  • output_format: "python" | "php": 输出为对应语言的字符串变量(主要是添加一些转义)

这其中比较有用的就是格式化打印以及输出 Python 来让 ORM 使用:

Python
sqlparse.format(sql, reindent_aligned=True, output_format='python')

# 输出是字符串
# "sql = ('SELECT *  '\n       '  FROM users;')\nsql2 = ('INSERT INTO users (id, name) VALUES (1, \\'Alice\\');')"

parse

这个 sqlparse 库最核心的接口,从他的库名称也能看出来。他使用起来非常简单:

Python
sql = "SELECT * FROM users; INSERT INTO users (id, name) VALUES (1, 'Alice');"
statements = sqlparse.parse(sql)

#(<Statement 'SELECT...' at 0x7F78951C08A0>, <Statement 'INSERT...' at 0x7F7895015160>)

他首先会将 SQL 语句分解,然后每条语句包装成一个 sqlparse.sql.Statement 对象,他表示一条完整的 SQL 语句的分析树,树上的每个叶子使用 Token 表示枝干使用 TokenList 表示。而继承自 TokenList 的其他对象表示 SQL 语句的各个部分,整个 sqlparse 库的对象的继承关系如下:

Bash
sqlparse.sql.Token
    - sqlparse.sql.TokenList
        - sqlparse.sql.Statement
        - sqlparse.sql.Identifier
        - sqlparse.sql.IdentifierList
        - sqlparse.sql.TypedLiteral
        - sqlparse.sql.Parenthesis
        - sqlparse.sql.SquareBrackets
        - sqlparse.sql.Assignment
        - sqlparse.sql.If
        - sqlparse.sql.For
        - sqlparse.sql.Comparison
        - sqlparse.sql.Comment
        - sqlparse.sql.Where
        - sqlparse.sql.Having
        - sqlparse.sql.Case
        - sqlparse.sql.Function
        - sqlparse.sql.Begin
        - sqlparse.sql.Operation
        - sqlparse.sql.Values
        - sqlparse.sql.Command

其中 Token 表示 SQL 语句的最小组成部分例如关键字、标识符、空白、运算符等等。而 TokenList 表示一组 Token 的集合。而更加具体的 Statement 表示 SQL 语句,Identifier 表示标识符等。因此所有的操作都绑定在 Token 和 TokenList 上。

Token

Token 表示 SQL 语句的最小组成部分:

Python
class Token:
    value: str # SQL 语句的原始值
    ttype: str # 类型

这其中最为特殊的就是 ttype,需要注意 TokenList 本身也继承自 Token,但是 TokenList.ttype 为 None,并且继承在其上的所有对象都为 None 。因此要记住ttype 只是针对于 SQL 语句的最小组成部分的 Token 而言的

Tips

TokenList.tokens 能够返回组成 TokenList 的所有 Token 这也是 TokenList 最常使用的接口。

sqlparse.tokens 的层次结构

所谓的层次结构就是叶的名称,他们对应的就是 Token.ttype:

  1. Text: 文本相关的类型
    • hitespace: 空白字符,如空格、制表符
    • Whitespace.Newline: 换行符
    • Comment: 注释
      • Comment.Single: 单行注释
      • Comment.Multiline: 多行注释
  2. Keyword: 关键字
    • Keyword: 一般关键字(例如 SELECT, FROM, INSERT 等)
    • Keyword.DML: 数据操作语言 (DML) 关键字(例如 SELECT, INSERT, UPDATE, DELETE)
    • Keyword.DDL: 数据定义语言 (DDL) 关键字(例如 CREATE, DROP, ALTER)
    • Keyword.TCL: 事务控制语言 (TCL) 关键字(例如 COMMIT, ROLLBACK)
    • Keyword.CTE: 公共表表达式 (WITH) 关键字
    • Keyword.Reserved: 保留字关键字
    • Keyword.Type: 数据类型关键字(例如 VARCHAR, INT)
  3. Name: 名称、标识符
    • Name: 一般的名称或标识符
    • Name.Builtin: 内置名称(例如函数名,如 COUNT)
    • Name.Constant: 常量名称
    • Name.Variable: 变量
    • Name.Function: 函数名称
    • Name.Class: 类名(一般用于数据库对象的类,如表名、视图名)
    • Name.Namespace: 命名空间
    • Name.Label: 标签
    • Name.Entity: 实体(用于对象或表)
    • Name.Builtin.Pseudo: 伪列(例如 Oracle 中的 ROWNUM)
  4. Literal: 字面值
    • Literal: 一般的字面值
    • Literal.String: 字符串字面值
      • Literal.String.Single: 单引号字符串
      • Literal.String.Symbol: 符号(例如运算符)
      • Literal.String.Heredoc: 多行字符串(用于某些 SQL 方言)
    • Literal.Number: 数字字面值
      • Literal.Number.Integer: 整数
      • Literal.Number.Float: 浮点数
      • Literal.Number.Hexadecimal: 十六进制数
  5. Operator: 运算符
    • Operator: 一般运算符(例如 +, -, *, /)
      • Operator.Comparison: 比较运算符(例如 =, <>, <=, >=)
  6. Punctuation: 标点符号
    • Punctuation: SQL 语句中的标点符号(例如逗号、括号)
  7. Wildcard: 通配符
    • Wildcard: 通配符(例如 *)
  8. Comparison: 比较符号
    • Comparison: 特定的比较符号(例如 =, !=)
  9. Error: 错误
    • Error: 标记为错误的令牌,通常是由于无法解析的 SQL 语句
  10. Other: 其他类型 - Other: 无法归类到其他类别的 token

一个使用场景就是如果我们想要获取所有的字面量(主要是 Value 子句中的),可以使用:

Python
# 所有的 tokens 都是 tuple 像 Other 里面只有一个 Other 而 Literal 就包含很多其他 ttype
from sqlparse.tokens import Literal


for token in token_list:
    if token.ttype in Literal:
        print(token.value)

Tips

由于 TokenList.tokens 返回的结果包含 Token 和 TokenList。需要注意 ttype 和 TokenList 子类的区别

TokenList

他表示一个树节点,其中最核心的就是 tokens 属性来返回其包含的所有 Token 对象:

Python
class TokenList:
    tokens: tuple(Token) # 包含的所有 Token

返回的 Token 如果不是最小的节点,那么可能依然是 TokenList,我们需要继续下分。看一下的示例:

Python
import sqlparse

sql_str = "INSERT INTO users (id, name, bio) VALUES (1, 'Alice', 'Hello, I\\'m a developer.');"

# parse 一定返回 tuple 因为上面只有一条语句因此需要 0 来提取该语句
stem = sqlparse.parse(sql_str)[0]
# 结果是 Statement 对象,他是 TokenList 的子类
<Statement 'INSERT...' at 0x7F78952840C0>
# 最常用的接口就是通过 TokenList.tokens 来获取所有的 Token
stem.tokens
# 结果是一个列表,其中包含了所有的 Token
[<DML 'INSERT' at 0x7F789525C280>,
 <Whitespace ' ' at 0x7F7895049540>,
 <Keyword 'INTO' at 0x7F7894FED120>,
 <Whitespace ' ' at 0x7F7894FEE080>,
 <Function 'users ...' at 0x7F7895284A60>,
 <Whitespace ' ' at 0x7F7894FED360>,
 <Values 'VALUES...' at 0x7F78952846E0>,
 <Punctuation ';' at 0x7F7894FEFAC0>]

# 上面的 DML、Whitespace、keyword 等都是 Token 这些名字对应了他们的 Token.ttype
# 而 Function 和 Values 都是 TokenList 对象他们可以继续下分

stem.tokens[6].tokens
[<Keyword 'VALUES' at 0x7F7895069900>,
<Whitespace ' ' at 0x7F7895069960>,
<Parenthesis '(1, 'A...' at 0x7F7895096270>]

# 可以看到返回的依然包含 Parenthesis 这个 TokenList 对象

Tips

这个库比较容易混淆的就是 ttype 和 TokenList 子类,他们的打印结果比较雷同