python的sql解析库-sqlparse

打印 上一主题 下一主题

主题 681|帖子 681|积分 2043

sqlparse 是一个 Python 库,是一个用于 Python 的非验证 SQL 解析器, 用于解析 SQL 语句并提供一个简单的 API 来访问解析后的 SQL 结构。可以资助解析复杂的 SQL 查询,提取信息,大概对 SQL 语句进行一些基本的分析和操纵。
  一、基本方法:

sqlparse的__init__方法中提供了四个基础方法
1.parse(sql)

用于将一个或多个 SQL 语句的字符串解析成 Python 对象,这些对象构成了一个抽象语法树(AST)
源码
  1. def parse(sql, encoding=None):
  2.     """Parse sql and return a list of statements.
  3.     :param sql: A string containing one or more SQL statements.
  4.     :param encoding: The encoding of the statement (optional).
  5.     :returns: A tuple of :class:`~sqlparse.sql.Statement` instances.
  6.     """
  7.     return tuple(parsestream(sql, encoding))
复制代码
按照符号分割sql后返回一个元组, 可以递归获取所有的值
  1. import sqlparse
  2. SQL = """CREATE TABLE foo (
  3.                  id integer primary key comment 'id_comm',
  4.                  title varchar(200) not null comment 'id_comm',
  5.                  description text comment 'id_comm');"""
  6. parsed = sqlparse.parse(SQL)[0]
  7. print(parsed)
复制代码
2.format(sql)

格式化代码, 返回格式化后的代码字符串
源码:
  1. def format(sql, encoding=None, **options):
  2.     """Format *sql* according to *options*.
  3.     Available options are documented in :ref:`formatting`.
  4.     In addition to the formatting options this function accepts the
  5.     keyword "encoding" which determines the encoding of the statement.
  6.     :returns: The formatted SQL statement as string.
  7.     """
复制代码
参数说明:


  • sql: 需要格式化的 SQL 语句字符串。
  • reindent=True: 自动重新缩进 SQL 语句,使代码块对齐。
  • keyword_case=‘upper’: 将 SQL 关键字转换为大写。可选值有’lower’、‘upper’ 或 ‘capitalize’。
  • 其他可选参数还包括 indent_width(用于设置缩进的空格数,默认为 2)、wrap_after(设置换行的字符数限定)等,以进一步定制输出样式。
  1. import sqlparse
  2. sql = """select * from tbl where id > 10;"""
  3. format = sqlparse.format(sql, reindent=True, keyword_case='upper')
  4. print(format)
  5. # SELECT *
  6. # FROM tbl
  7. # WHERE id > 10;
复制代码
3.split()

按照符号分割sql语句, 返回一个sql列表
源码:
  1. def split(sql, encoding=None):
  2.     """Split *sql* into single statements.
  3.     :param sql: A string containing one or more SQL statements.
  4.     :param encoding: The encoding of the statement (optional).
  5.     :returns: A list of strings.
  6.     """
复制代码
  1. import sqlparse
  2. sql = """select * from tbl where id > 10;select * from tbl where id > 20;"""
  3. split = sqlparse.split(sql)
  4. print(split)
  5. # ['select * from tbl where id > 10;', 'select * from tbl where id > 20;']
复制代码
4.parsestream()

类似parse方法, 流式解析sql, 它的计划初志是为了处理从流式输入(如文件、网络连接或任何可迭代的对象)读取的 SQL 代码,而不是一次性加载整个 SQL 字符串到内存中。如许,在处理大型 SQL 文件或连续的数据流时,可以更有效地管理内存。
源码:
  1. def parsestream(stream, encoding=None):
  2.     """Parses sql statements from file-like object.
  3.     :param stream: A file-like object.
  4.     :param encoding: The encoding of the stream contents (optional).
  5.     :returns: A generator of :class:`~sqlparse.sql.Statement` instances.
  6.     """
复制代码
  1. with open('../static/pre_sql.sql', 'r', encoding='utf-8') as file:
  2.     for statement in sqlparse.parse(file):
  3.         print(statement)
复制代码
二、Token

源码:
  1. class Token:
  2.     """Base class for all other classes in this module.
  3.     It represents a single token and has two instance attributes:
  4.     ``value`` is the unchanged value of the token and ``ttype`` is
  5.     the type of the token.
  6.     """
  7.    
  8.     def __init__(self, ttype, value):
  9.     value = str(value)
  10.     self.value = value
  11.     self.ttype = ttype
  12.     self.parent = None
  13.     self.is_group = False
  14.     self.is_keyword = ttype in T.Keyword
  15.     self.is_whitespace = self.ttype in T.Whitespace
  16.     self.normalized = value.upper() if self.is_keyword else value
复制代码
sqlparse.sql.Token: 这是最基本的Token类,表示SQL语句中的一个原子部门,如一个单词大概符号。它包含以部属性:


  • value: 该Token的实际文本内容,好比一个关键字像SELECT或一个标识符如表名。
  • token_type: 表示Token类型的枚举值,好比Keyword、Identifier、Punctuation等。
  • position 或 start_pos: 表示Token在原始SQL文本中的起始位置信息,有助于追踪Token的来源。
    相干Token子类和概念
  • sqlparse.sql.Identifier: 专门表示SQL中的标识符,如表名、列名等。这类Token可能会有额外的属性来表示是否为 quoted identifier(被引号困绕的标识符)。
  • sqlparse.sql.Keyword: 表示SQL关键字,如SELECT, FROM, WHERE等。
  • sqlparse.sql.Punctuation: 表示SQL中的标点符号,如逗号,、分号;等。
  • sqlparse.sql.Comment: 用于表示SQL中的解释内容,可以是行内解释(-- …)或块解释(/* … */)。
  • sqlparse.sql.Comparison: 包含比力操纵符(如=, !=, IN, BETWEEN等)以及它们双方的操纵数,用于构建更复杂的表达式分析。
  • sqlparse.sql.Statement: 表示整个SQL语句,通常是由多个Token和其他Statement对象构成的树状结构,便于递归遍历整个SQL语句的结构。
    这里就需要引入sql解析的过程
   sql -> 语法分析器(Lexer) -> Token流 -> 语法分析器(Parse) -> 抽象语法树(AST) -> 树结构(Tree Parse)
  每个解析结果都会附带一个tokens 的属性,它是一个生成器,用于迭代解析后的Token序列, 包含了一些类型信息, 其中的类型信息有:
  1. # Special token types
  2. Text = Token.Text
  3. Whitespace = Text.Whitespace
  4. Newline = Whitespace.Newline
  5. Error = Token.Error
  6. # Text that doesn't belong to this lexer (e.g. HTML in PHP)
  7. Other = Token.Other
  8. # Common token types for source code
  9. Keyword = Token.Keyword
  10. Name = Token.Name
  11. Literal = Token.Literal
  12. String = Literal.String
  13. Number = Literal.Number
  14. Punctuation = Token.Punctuation
  15. Operator = Token.Operator
  16. Comparison = Operator.Comparison
  17. Wildcard = Token.Wildcard
  18. Comment = Token.Comment
  19. Assignment = Token.Assignment
  20. # Generic types for non-source code
  21. Generic = Token.Generic
  22. Command = Generic.Command
  23. # String and some others are not direct children of Token.
  24. # alias them:
  25. Token.Token = Token
  26. Token.String = String
  27. Token.Number = Number
  28. # SQL specific tokens
  29. DML = Keyword.DML
  30. DDL = Keyword.DDL
  31. CTE = Keyword.CTE
复制代码
  1. Text: 基础文本类型,通常用于表示SQL语句中的普通文本部分。
  2.     Whitespace: 空白字符,包括空格、制表符等,用于分隔SQL语句的不同部分。
  3.         Newline: 特指换行符,用于标识新的一行开始。
  4.     Error: 表示解析过程中遇到的无法识别或错误的文本。
  5.     Other: 表示不属于当前解析器(如SQL解析器)预期的文本,例如在嵌入式SQL中可能遇到的其他语言(如HTML在PHP中的情况)。
  6. Keyword: SQL关键字,如 SELECT, FROM, WHERE 等。
  7.     DML: 数据操作语言(Data Manipulation Language)关键字,如 INSERT, UPDATE, DELETE, SELECT。
  8.     DDL: 数据定义语言(Data Definition Language)关键字,如 CREATE, ALTER, DROP。
  9.     CTE: 公共表达式(Common Table Expression)关键字,如 WITH。
  10. Name: 数据库对象名称,如表名、列名等。
  11. Literal: 字面量值,直接写在SQL中的数据值。
  12.     String: 字符串字面量,如 'example string'。
  13.     Number: 数字字面量,如 42, 3.14。
  14. Punctuation: 标点符号,如逗号、括号等,用于分隔或包围SQL的各个部分。
  15. Operator: 操作符,如 +, -, *, /, = 等。
  16.     Comparison: 比较操作符,如 =, !=, <, > 等。
  17. Wildcard: 通配符,如 % 在某些SQL上下文中的使用。
  18. Comment: 注释,SQL中的单行或多行注释。
  19. Assignment: 赋值操作符,如 := 在某些SQL方言中用于赋值。
  20. Generic: 通用类型,适用于非特定源代码的分隔。
  21.     Command: 命令,可能特指一些SQL命令或交互式shell命令。
复制代码
  1. Whitespace:空白字符(如空格、制表符、换行符等)
  2. Keyword:SQL 关键字(如 SELECT、FROM、WHERE 等)
  3. Name:标识符(如表名、列名等)
  4. String.Single:单引号字符串字面量
  5. String.Double:双引号字符串字面量(在某些 SQL 方言中用于标识符)
  6. String.Backtick:反引号字符串字面量(如 MySQL 中的表名和列名)
  7. Identifier: 表示SQL中的标识符,包括但不限于表名、列名、数据库名等。
  8. Compound: 复合Token,可能包含多个子Token,用于更复杂的结构,如 Case 语句、 When 条件等。
  9. Number.Integer:整数
  10. Number.Float:浮点数
  11. Number.Hex:十六进制数
  12. Operator:操作符(如 =、<>、+、- 等)
  13. Punctuation:标点符号(如逗号、分号、括号等)
  14. Comment.Single:单行注释
  15. Comment.Multiline:多行注释
  16. Wildcard:通配符(如 *)
  17. Function:函数名(如 COUNT()、MAX() 等)
  18. DML、DDL、DCL 等:表示数据操作语言、数据定义语言、数据控制语言等的高级分类
复制代码
三、其他类型

有些属于token的属性
但有些不属于token, 好比Where、IdentifierList、Identifier、Parenthesis、Comment等
  1. sql = 'select 1 as id, name, case when name = "" then 3 else 4 end as score from tbl where id > 10 limit 100'
  2. stmts = sqlparse.parse(sql)[0].tokens
  3. for stmt in stmts:
  4.     print(f"{type(stmt)}::{stmt.ttype}::",stmt)
  5. # <class 'sqlparse.sql.Token'>::Token.Keyword.DML:: select
  6. # <class 'sqlparse.sql.Token'>::Token.Text.Whitespace::  
  7. # <class 'sqlparse.sql.IdentifierList'>::None:: 1 as id, name, case when name = "" then 3 else 4 end as score
  8. # <class 'sqlparse.sql.Token'>::Token.Text.Whitespace::  
  9. # <class 'sqlparse.sql.Token'>::Token.Keyword:: from
  10. # <class 'sqlparse.sql.Token'>::Token.Text.Whitespace::  
  11. # <class 'sqlparse.sql.Identifier'>::None:: tbl
  12. # <class 'sqlparse.sql.Token'>::Token.Text.Whitespace::  
  13. # <class 'sqlparse.sql.Where'>::None:: where id > 10
  14. # <class 'sqlparse.sql.Token'>::Token.Keyword:: limit
  15. # <class 'sqlparse.sql.Token'>::Token.Text.Whitespace::  
  16. # <class 'sqlparse.sql.Token'>::Token.Literal.Number.Integer:: 100
复制代码
  当查询有多列大概有多表时, 会将其封装为IdentifierList, 单表时候会被封装为Identifier, 过滤条件被封装为Where, 括号会被封装为Parenthesis, 解释会被封装为Comment
  四、案例: 提取所有查询的字段和表名

  1. import sqlparse
  2. import re
  3. sql = 'insert into table inser_tbl partition (dt = dt) select 1 as id, name, case when (name = "" or name = "") then 3 else 4 end as score from tbl where id > 10 limit 100'
  4. stmts = sqlparse.parse(sql)[0].tokens
  5. cols = []
  6. tbls = []
  7. froms = []
  8. wheres = []
  9. last_key = ''
  10. for stmt in stmts:
  11.     if stmt.value == 'insert' or stmt.value == 'select' or stmt.value == 'from':
  12.         last_key = stmt.value
  13.     # 剔除空格和换行
  14.     if stmt.ttype is sqlparse.tokens.Text.Whitespace:
  15.         continue
  16.     # 关键字
  17.     elif stmt.ttype is sqlparse.tokens.Keyword.DML:
  18.         dml = stmt.value
  19.         last_key = dml
  20.     # 字段
  21.     elif isinstance(stmt, sqlparse.sql.IdentifierList):
  22.         # 判断上一个是什么类型
  23.         if last_key == 'select':
  24.             for identifier in stmt.get_identifiers():
  25.                 col_name = identifier.value
  26.                 if re.search('as', col_name, re.I):
  27.                     col_name = re.search('as (.*)', col_name, re.I).group(1).strip()
  28.                 cols.append(col_name)
  29.         elif last_key == 'from':
  30.             for identifier in stmt.get_identifiers():
  31.                 froms.append(identifier.value)
  32.         else:
  33.             for identifier in stmt.get_identifiers():
  34.                 tbls.append(identifier.value)
  35.     elif isinstance(stmt, sqlparse.sql.Identifier):
  36.         if last_key == 'select':
  37.             cols.append(stmt.value)
  38.         elif last_key == 'from':
  39.             froms.append(stmt.value)
  40.         else:
  41.             tbls.append(stmt.value)
  42.     elif isinstance(stmt, sqlparse.sql.Where):
  43.         wheres.append(stmt.value)
  44.     # 表名
  45. print("cols:", cols)
  46. print("tbls:", tbls)
  47. print("froms:", froms)
  48. print("wheres:", wheres)
  49. # cols: ['id', 'name', 'score']
  50. # tbls: ['inser_tbl']
  51. # froms: ['tbl']
  52. # wheres: ['where id > 10 ']
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

知者何南

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表