Sqlparse

警告
本文最后更新于 2021-08-19,文中内容可能已过时。

本章内容仅使用最简单的方法,并且参考了网上教程,适用于自己的项目,仅刚大家参考

SQL解析

简介

sqlparse是一个无验证的SQL解析器。它提供了解析、拆分、格式化SQL语句的能力。

代码:https://github.com/andialbrecht/sqlparse

sqlparse提供了三个基本的函数,用于SQL语句处理。

  • split

    • 拆分包含多个SQL语句的字符串为SQL语句列表。
    • 语句结尾通过;分隔。
1
2
3
4
import sqlparse
sql = 'select * from foo; select * from bar;'
sqlparse.split(sql)
[u'select * from foo; ', u'select * from bar;']
  • format
    • format函数接受关键字参数
      • keyword_case关键词upperlowersql的保留字大小写
      • identifier_case标识符的upper、lower大小写
      • strip_comments=Ture删除注释
      • reindent=Ture美化sq缩进语句发生改变
    • 将SQL语句格式化,以便更清晰的展现。
1
2
3
4
5
6
7
8
sql = 'select * from foo where id in (select id from bar);'
print(sqlparse.format(sql, reindent=True, keyword_case='upper'))
# 结果
SELECT *
FROM foo
WHERE id IN
  (SELECT id
   FROM bar);
  • parse
    • parse()返回sql解析结果tuple
    • tuple的每个元素对应于split()的一个SQL语句。
    • 解析的结果也可以通过str()生成原始的SQL语句。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
sql='select * from bar where id=1 limit 1000,2000;'
parsed = sqlparse.parse(sql)
stmt = parsed[0]

stmt.tokens
# (<DML 'select' at 0x9b63c34>,
#  <Whitespace ' ' at 0x9b63e8c>,
#  <Operator '*' at 0x9b63e64>,
#  <Whitespace ' ' at 0x9b63c5c>,
#  <Keyword 'from' at 0x9b63c84>,
#  <Whitespace ' ' at 0x9b63cd4>,
#  <Identifier '"somes...' at 0x9b5c62c>,
#  <Whitespace ' ' at 0x9b63f04>,
#  <Where 'where ...' at 0x9b5caac>)

str(stmt)  # str(stmt) for Python 3
# 'select * from "someschema"."mytable" where id = 1'

str(stmt.tokens[-1])  # or just the WHERE part
# 'where id = 1'

基类

所有返回的对象都从这些基类继承。在Token此类表示单个令牌和 TokenList类是一组令牌。后者提供了检查其子类的方法

Token

1
sqlparse.sql.Tokenttypevalue 

它表示单个标记,并具有两个实例属性:value标记的未更改值,是标记ttype的类型。

  1. flatten()解决分组
  2. has_ancestor(other)如果其他的再其父类中,则返回
  3. is_child_of(other)如果标记是其他标记的子标记,则返回
  4. match(ttype, values, regex=False)
  5. within(group_cls)
1
2
3
4
5
6
sql = 'select * from "someschema"."mytable" where id = 1'

parsed = sqlparse.parse(sql)
stmt = parsed[0].tokens
 for token in stmt:
	 print(token.ttype, token.value)

返回结果如下

Token.Keyword.DML select
Token.Text.Whitespace  
Token.Wildcard *
Token.Text.Whitespace  
Token.Keyword from
Token.Text.Whitespace  
None "someschema"."mytable"
Token.Text.Whitespace  
None where id = 1
Token子类类型
  1. sqlparse.sql.Token
  2. sqlparse.sql.Where
  3. sqlparse.sql.Identifier
  4. sqlparse.sql.IdentifierList
token.ttype属性(sqlparse.sql.Token)
  1. Token.Keyword.DDL标记语法create语句
  2. Token.Keyword.DMLSELECT标记法 select、updata、delete
  3. Token.Keyword SQL保留的关键字
  4. Token.Text.Whitespace空格,sql语句间的空格
  5. Token.Text.Whitespace.Newline sql新的一行间空格
  6. Token.Punctuation 结束标点通常为;

TokenList

一组令牌,t其有一个额外的属性tokens,包含子标记列表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
flatten()
get_alias()
get_name()
get_parent_name()
get_token_at_offset(offset)
group_tokens(grp_cls, start, end, include_end=True, extend=False)
has_alias()
insert_after(where, token, skip_ws=True)
insert_before(where, token)
token_first(skip_ws=True, skip_cm=False)
token_index(token, start=0)
token_next(idx, skip_ws=True, skip_cm=False, _reverse=False)
token_prev(idx, skip_ws=True, skip_cm=False)

SQL表示类(SQL语句的不同部分)

判断sql的类型DML/DDL

1
sqlparse.sql.Statementtokens = None 

get_type()语句的类型

1
2
 sql = sqlparse.sql.Statement(stmt)
 print(sql.get_type())

返回SQL标记关键字SELECTselectupdatadeletecreate

Identifier标识符

1
sqlparse.sql.Identifiertokens = None 

get_array_indices()返回索引标记列表的迭代器 get_ordering()返回排序或None大写字符串 get_typecastNone以字符串形式返回类型转换或此对象 is_wildcardTrue如果此标识符包含通配符,则返回

IdentifierList标识符列表

1
sqlparse.sql.IdentifierList(tokens = None)

get_identifiers返回标识符, 不包含空格和标点符号

where语句

1
sqlparse.sql.Wheretokens = None 

一个或多个WHEN且可能是ELSE部分的CASE语句

1
sqlparse.sql.Casetokens = None 

get_cases(skip_ws = False )返回两个元组的列表(条件,值)

括号之间的标记

1
sqlparse.sql.Parenthesistokens = None 

** 带有else ifelse部分的if子句**

1
sqlparse.sql.If(tokens=None)

FOR循环

1
sqlparse.sql.Fortokens = None 

var:= val;这样的赋值

1
 sqlparse.sql.Assignmenttokens = None 

WHERE子句中使用的比较

1
  sqlparse.sql.Assignmenttokens = None 

解析limit后的字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import sqlparse
from sqlparse.tokens import Keyword, Number
from sqlparse.sql import IdentifierList, Identifier


def extract_from_part(parsed):
    from_seen = False
    for item in parsed.tokens:
        if from_seen:
            if item.ttype is Keyword:
                return
            else:
                yield item
        elif item.ttype is Keyword and item.value.upper() == 'LIMIT':
            from_seen = True

def extract_limit_identifiers(token_stream):
    for item in token_stream:
        if isinstance(item, IdentifierList):
            for identifier in item.get_identifiers():
                yield identifier.value
        elif item.ttype is Number.Integer:
            yield item.value
        elif item.ttype is Keyword:
            yield item.value

# 通过传入sql语句,解析出limit后的数值
# 例如:limit 100,200,返回值[100,200]
def extract_limit(sql):
    stream = extract_from_part(sqlparse.parse(sql)[0])
    return list(extract_limit_identifiers(stream))

增加where筛选

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import sqlparse


def add_where(sql: str, k_and_v: dict):
    # k_and_v:{'num >= ':'200'}
    stmt = sqlparse.parse(sql)[0]
    add_in_where = ''
    for k, v in k_and_v.items():
        add_in_where += 'and {}{} '.format(k, v)
    sql_tmp = []
    for i in stmt.tokens:
        if 'WHERE' in i.value.upper():
            sql_tmp.append('{}{}'.format(i.value, add_in_where))
        sql_tmp.append(i.value)
    sql = ''.join(sql_tmp)
    if 'WHERE' not in sql.upper():
        sql = sql.replace('limit', 'where {} limit'.format(add_in_where))
0%