Python-sqlparse解析SQL工具库一文详解(一)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Python-sqlparse解析SQL工具库一文详解(一)

前言


写此sqlparse库的目的还是寻找在python编程内可行的SQL血缘解析,JAVA去解析Hive的源码实践的话我还是打算放到后期来做,先把Python能够实现的先实现完。上篇系列讲述的基于antrl解析说是用python其实还是太牵强了,无非就是使用PyJnius调用JAVA的类方法来实现,没有多大的意义来牵扯到Python编程。主要是HiveSQL的底层就是JAVA代码,怎么改写还是绕不开JAVA的。不过上篇系列我有提到过sqlparse,其实这个库用来解析血缘的话也不是不可以,但是能够实现的功能是有限的,目前我实验还行,一些复杂超过千行的数据分析SQL没有测试过。做一些简单的血缘解析的话还是没有应该太大问题,后续我会在此基础之上开发尝试。


一、sqlparse简介


首先先给官网地址:python-sqlparse。有足够好编码能力可以直接上github上面看源码,解读更细:github.sqlparse


sqlparse是用于Python的非验证SQL解析器。它支持解析、拆分和格式化SQL语句。既然有解析功能那么我们就能做初步的血缘解析功能。这个库的函数解析没有像Pandas和numpy写的那么详细,毕竟是人家个人的开源库,功能写的已经很不错了,能够省去我们很多递归剥离AST树的时间。官网上关于该库使用操作很简单,很多比较好的功能函数也没有使用到,我希望可以尽力将此库开发为通用SQL血缘解析的基础工具库。如果该功能开发完我会将此项目开源。


我通过细读源码来了解此库的大体功能。


二、功能代码解析


1.初始方法


看初始化代码方法有四种:parse,parsestream,format,split这四种


1.parse

def parse(sql, encoding=None):
    """Parse sql and return a list of statements.
    :param sql: A string containing one or more SQL statements.
    :param encoding: The encoding of the statement (optional).
    :returns: A tuple of :class:`~sqlparse.sql.Statement` instances.
    """
    return tuple(parsestream(sql, encoding))

传入一个SQL语句,返回一个 sqlparse.sql.Statement的元组,我们可以递归方式获得输出。

query = 'Select a, col_2 as b from Table_A;'
for each in sqlparse.parse(query):
    print(each)

0a1458538e3f4fb78e7fd47030c7d924.png

其元组根据;符号来进行切分存储:

query = 'Select a, col_2 as b from Table_A;select * from foo'
for each in sqlparse.parse(query):
    print(each)

1ad1766b825747bfbb74c483ec01dfd8.png


f034e91b8a3642d7a3247c1c81c8952a.png


2.parsestream


可以看到第一个方法是调用了parsestream来完成流式解析的,那么这个方法也就是循环读取sql语句来完成转换statment的:

def parsestream(stream, encoding=None):
    """Parses sql statements from file-like object.
    :param stream: A file-like object.
    :param encoding: The encoding of the stream contents (optional).
    :returns: A generator of :class:`~sqlparse.sql.Statement` instances.
    """
    stack = engine.FilterStack()
    stack.enable_grouping()
    return stack.run(stream, encoding)

这里的引擎是可以替换的。


sqlparse.parsestream(query)

24dafe92dbdc4a049fc4ea6e4ced8699.png


它将返回一个sqlparse.sql.Statement实例的发生器。来看看这个run方法:


def run(self, sql, encoding=None):
        stream = lexer.tokenize(sql, encoding)
        # Process token stream
        for filter_ in self.preprocess:
            stream = filter_.process(stream)
        stream = StatementSplitter().process(stream)
        # Output: Stream processed Statements
        for stmt in stream:
            if self._grouping:
                stmt = grouping.group(stmt)
            for filter_ in self.stmtprocess:
                filter_.process(stmt)
            for filter_ in self.postprocess:
                stmt = filter_.process(stmt)
            yield stmt

该方法就是生产一个statment,这个类应该就是这个库的基类了,多半围绕这个数据结构来处理。


3.format


该方法就是将sql语句标准化:


query = 'Select a, col_2 as b from Table_A;select * from foo'
print(sqlparse.format(query, reindent=True, keyword_case='upper'))

48739bf7c2dd49ae979f426a7b047fe7.png


format()函数接受关键字参数:


   keyword_case 关键词upper、lowersql的保留字大小写

   identifier_case 标识符的upper、lower大小写

   strip_comments=Ture删除注释

   reindent=Ture美化sq缩进语句发生改变


4.split


该方法用于分割sql语句:


sqlparse.split(query)


55cfa6d9345047d6996bfa37a4345568.png


这里补充一下calss类sqlparse.sql.Statement是可以直接通过str转换为字符串的。

结果返回一个分割后的list。至此初始方法就写完了,下面我将详解一下基类,这将决定是我们是否能灵活运用此库。


2.基类-Token


我们来看看Token的初始方法属性:


def __init__(self, ttype, value):
        value = str(value)
        self.value = value
        self.ttype = ttype
        self.parent = None
        self.is_group = False
        self.is_keyword = ttype in T.Keyword
        self.is_whitespace = self.ttype in T.Whitespace
        self.normalized = value.upper() if self.is_keyword else value


这个Token类也就是语法解析器的重点数据流了:

5ee0df906fcf4cf39f29c525e8c28974.png

此类需要生成Tokens使用,这牵扯到另一个方法tokens.py:

此方法也就是将statment类转换为Token流:

parsed = sqlparse.parse(query)
stmt = parsed[0]
stmt.tokens


7dbc3a7fa580452ba005bc11d4b5367d.png


其中我们需要解析的每个Token的标识码也就是第一个ttype属性,解析之后:


1. for each_token in sql_tokens:
2. print(each_token.ttype,each_token.value)


f84a5abe5dd647c0af6ecf23714782aa.png


我们拿一个Token来研究就能逐渐解析到其他token。我们建立一个列表将其主要属性ttype和value收集起来:

type(list_ttype[0])
type(list_value[0])

第一个属性为sqlparse.tokens._TokenType第二个value直接就是str了。上tokens看_TokenType:

# Special token types
Text = Token.Text
Whitespace = Text.Whitespace
Newline = Whitespace.Newline
Error = Token.Error
# Text that doesn't belong to this lexer (e.g. HTML in PHP)
Other = Token.Other
# Common token types for source code
Keyword = Token.Keyword
Name = Token.Name
Literal = Token.Literal
String = Literal.String
Number = Literal.Number
Punctuation = Token.Punctuation
Operator = Token.Operator
Comparison = Operator.Comparison
Wildcard = Token.Wildcard
Comment = Token.Comment
Assignment = Token.Assignment
# Generic types for non-source code
Generic = Token.Generic
Command = Generic.Command
# String and some others are not direct children of Token.
# alias them:
Token.Token = Token
Token.String = String
Token.Number = Number
# SQL specific tokens
DML = Keyword.DML
DDL = Keyword.DDL
CTE = Keyword.CTE

可以发现这就是Token的识别解析类型码,通过该码就可以访问获得解析出的关键字了。

关于此基类又有五种主要的方法:


1.flatten()


用于解析子组

for each_token in sql_tokens:
    #list_ttype.append(each_token.ttype),list_value.append(each_token.value)
    print(each_token.flatten())

5c80ab4c17a54220900792fe7c24145e.png

2.match(ttype, values, regex=False)


检查标记是否与给定参数匹配。

list_ttype=[]
list_value=[]
for each_token in sql_tokens:
    #list_ttype.append(each_token.ttype),list_value.append(each_token.value)
    print(each_token.match(each_token.ttype,each_token.ttype))

1e9e2149eca641308c75c520fca279bc.png

or运算为None匹配为True输出。


ttype是一种token类型。如果此标记与给定的标记类型不匹配。values是此标记的可能值列表。这些values一起进行OR运算,因此如果只有一个值与True匹配,则返回。除关键字标记外,比较区分大小写。为了方便起见,可以传入单个字符串。如果regex为True(默认值为False),则给定值将被视为正则表达式。


另外还有三种方法has_ancestor(other),is_child_of(other),within(group_cls)这都有调用功能函数相关,可以先不用了解。


由此Token传入流单体已经差不多分析完,但是AST树该如何生成这是个问题,还有关于树的递归问题和层级问题,我们继续根据基类来慢慢摸清。这篇文章已经足够多内容了,先打住。下一篇再细讲。

目录
相关文章
|
17天前
|
安全 程序员 API
|
11天前
|
算法 Python
Python 大神修炼手册:图的深度优先&广度优先遍历,深入骨髓的解析
在 Python 编程中,掌握图的深度优先遍历(DFS)和广度优先遍历(BFS)是进阶的关键。这两种算法不仅理论重要,还能解决实际问题。本文介绍了图的基本概念、邻接表表示方法,并给出了 DFS 和 BFS 的 Python 实现代码示例,帮助读者深入理解并应用这些算法。
25 2
|
11天前
|
自然语言处理 并行计算 数据可视化
免费开源法律文档比对工具:技术解析与应用
这款免费开源的法律文档比对工具,利用先进的文本分析和自然语言处理技术,实现高效、精准的文档比对。核心功能包括文本差异检测、多格式支持、语义分析、批量处理及用户友好的可视化界面,广泛适用于法律行业的各类场景。
|
20天前
|
测试技术 开发者 Python
深入浅出:Python中的装饰器解析与应用###
【10月更文挑战第22天】 本文将带你走进Python装饰器的世界,揭示其背后的魔法。我们将一起探索装饰器的定义、工作原理、常见用法以及如何自定义装饰器,让你的代码更加简洁高效。无论你是Python新手还是有一定经验的开发者,相信这篇文章都能为你带来新的启发和收获。 ###
12 1
|
20天前
|
设计模式 测试技术 开发者
Python中的装饰器深度解析
【10月更文挑战第24天】在Python的世界中,装饰器是那些能够为函数或类“添彩”的魔法工具。本文将带你深入理解装饰器的概念、工作原理以及如何自定义装饰器,让你的代码更加优雅和高效。
|
30天前
|
XML 前端开发 数据格式
Beautiful Soup 解析html | python小知识
在数据驱动的时代,网页数据是非常宝贵的资源。很多时候我们需要从网页上提取数据,进行分析和处理。Beautiful Soup 是一个非常流行的 Python 库,可以帮助我们轻松地解析和提取网页中的数据。本文将详细介绍 Beautiful Soup 的基础知识和常用操作,帮助初学者快速入门和精通这一强大的工具。【10月更文挑战第11天】
56 2
|
30天前
|
数据安全/隐私保护 流计算 开发者
python知识点100篇系列(18)-解析m3u8文件的下载视频
【10月更文挑战第6天】m3u8是苹果公司推出的一种视频播放标准,采用UTF-8编码,主要用于记录视频的网络地址。HLS(Http Live Streaming)是苹果公司提出的一种基于HTTP的流媒体传输协议,通过m3u8索引文件按序访问ts文件,实现音视频播放。本文介绍了如何通过浏览器找到m3u8文件,解析m3u8文件获取ts文件地址,下载ts文件并解密(如有必要),最后使用ffmpeg合并ts文件为mp4文件。
|
9天前
|
SQL 监控 安全
员工上网行为监控软件:SQL 在数据查询监控中的应用解析
在数字化办公环境中,员工上网行为监控软件对企业网络安全和管理至关重要。通过 SQL 查询和分析数据库中的数据,企业可以精准了解员工的上网行为,包括基础查询、复杂条件查询、数据统计与分析等,从而提高网络管理和安全防护的效率。
22 0
|
1月前
|
SQL 监控 数据库
SQL语句是否都需要解析及其相关技巧和方法
在数据库管理中,SQL(结构化查询语言)语句的使用无处不在,它们负责数据的查询、插入、更新和删除等操作
|
11天前
|
SQL 数据挖掘 Python
数据分析编程:SQL,Python or SPL?
数据分析编程用什么,SQL、python or SPL?话不多说,直接上代码,对比明显,明眼人一看就明了:本案例涵盖五个数据分析任务:1) 计算用户会话次数;2) 球员连续得分分析;3) 连续三天活跃用户数统计;4) 新用户次日留存率计算;5) 股价涨跌幅分析。每个任务基于相应数据表进行处理和计算。

热门文章

最新文章