当前位置: 首页>編程日記>正文

实用SQL代码解析工具——sqlparse

实用SQL代码解析工具——sqlparse

1 引言

一个数据分析团队往往会积累大量基于SQL的代码,用于日常的报表,模型数据提取,业务决策等等。有时随着公司的发展和技术更替,公司的数据仓库会进行迁移或重构,当表结构,字段名或者表名发生变化时,包含这些表的SQL代码就需要相应地进行改写。人为改写一段段业务代码,尤其是对字段或者表名的修改,往往比较重复而且容易遗漏。

懒惰是程序员的第一生产力,既然是重复的工作,那么有没有什么工具可以帮助我们自动化这一过程呢?

2 sqlparse开源库

2.1 介绍

想要改写SQL代码,关键的一步是对SQL进行解析。sqlparse是基于Python的一个无验证解析器,他提供了一个简单的parse()函数来返回类似语法树的解析结构。我们用_pprint_tree()函数打印下解析后的SQL语句:

import sqlparse 
query = 'Select a, col_2 as b from Table_A;'
sqlparse.parse(query)[0]._pprint_tree()

输出为:

|- 0 DML 'Select'
|- 1 Whitespace ' '
|- 2 IdentifierList 'col_1,...'
|  |- 0 Identifier 'col_1'
|  |  `- 0 Name 'col_1'
|  |- 1 Punctuation ','
|  |- 2 Whitespace ' '
|  `- 3 Identifier 'col_2 ...'
|     |- 0 Name 'col_2'
|     |- 1 Whitespace ' '
|     |- 2 Keyword 'as'
|     |- 3 Whitespace ' '
|     `- 4 Identifier 'b'
|        `- 0 Name 'b'
|- 3 Whitespace ' '
|- 4 Keyword 'from'
|- 5 Whitespace ' '
|- 6 Identifier 'Table_A'
|  `- 0 Name 'Table_A'
`- 7 Punctuation ';'

可以看到sqlparse可以准确的识别出查询语句中的关键词,并且字段,表名被识别成了Identifier类型。结合前后token中的关键词就可以进一步判断出具体是字段还是表名。在此之前还需要了解各种类型包含的各种方法。

2.2 类型定义

sqlparse的基础类型是Token,其中ttype和value两个常用属性。此外类似树结构的节点,他可以通过parent属性关联上一层token。它的常用方法主要是对该token属性的访问和判断:

class sqlparse.sql.Token(ttype, value):

  • flatten(): Resolve subgroups.

  • has_ancestor(other): Returns True if other is in this tokens ancestry.

  • is_child_of(other): Returns True if this token is a direct child of other.

  • match(ttype, values, regex=False): checks whether the token matches the given arguments.

  • within(group_cls): Returns True if this token is within group_cls.

TokenList是Token类型的继承,定义为一群token的集合。通过token.tokens属性来访问。如例子中的'col_2 as b'就被判定为了Identifier类型的TokenLis他。除了继承和部分覆写了Token类型的方法以外,它还定义了获取子token位置,名称,匹配搜索子token等方法:

class sqlparse.sql.TokenList(tokens=None):

  • flatten(): Generator yielding ungrouped tokens. This method is recursively called for all child tokens. (覆写了flatten方法)

  • get_alias(): Returns the alias for this identifier or None.

  • get_name(): Returns the name of this identifier.

  • group_tokens(grp_cls, start, end, include_end=True, extend=False): Replace tokens by an instance of grp_cls.

  • has_alias(): Returns True if an alias is present.

  • token_first(skip_ws=True, skip_cm=False): Returns the first child token.

  • token_index(token, start=0): Return list index of token.

  • token_prev(idx, skip_ws=True, skip_cm=False): Returns the previous token relative to idx.*

2.3 词法解析

对于SQL中的DDL(Data Definition Language,数据定义语言)/DML(Data Manipulation Language,数据操纵语言)等关键词,sqlparse主要通过正则表达式识别,所有的正则表达与token类型的对应关系储存在keywords.py里的SQL_REGEX变量中,必要时可以修改正则表达来适应不同的数据仓库语法和函数。

3 案例:从查询中提取表名

sqlparse作者在源码中提供了提取表名的范例,主要思路是在解析过程中遇到关键词from或者join后,提取其后的tokenList。

ALL_JOIN_TYPE = ('LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'FULL JOIN', 'LEFT OUTER JOIN', 'FULL OUTER JOIN')def is_subselect(parsed):"""是否子查询:param parsed: T.Token"""if not parsed.is_group:return Falsefor item in parsed.tokens:if item.ttype is DML and item.value.upper() == 'SELECT':return Truereturn Falsedef extract_from_part(parsed):"""提取from之后模块"""from_seen = Falsefor item in parsed.tokens:if from_seen:if is_subselect(item):for x in extract_from_part(item):yield xelif item.ttype is Keyword:from_seen = Falsecontinueelse:yield itemelif item.ttype is Keyword and item.value.upper() == 'FROM':from_seen = Truedef extract_join_part(parsed):"""提取join之后模块"""flag = Falsefor item in parsed.tokens:if flag:if item.ttype is Keyword:flag = Falsecontinueelse:yield itemif item.ttype is Keyword and item.value.upper() in ALL_JOIN_TYPE:flag = Truedef extract_table_identifiers(token_stream):for item in token_stream:if isinstance(item, IdentifierList):for identifier in item.get_identifiers():yield identifier.get_name()elif isinstance(item, Identifier):yield item.get_name()elif item.ttype is Keyword:yield item.valuedef extract_tables(sql):"""提取sql中的表名(select语句)"""from_stream = extract_from_part(sqlparse.parse(sql)[0])join_stream = extract_join_part(sqlparse.parse(sql)[0])return list(extract_table_identifiers(from_stream)) + list(extract_table_identifiers(join_stream))

4 总结

sqlparse是一个比较强大的基于python语言的SQL解析工具,开源库在GitHub上获得了2.6k个星星和522次Fork。其代码简洁高效,结构清晰,值得感兴趣的同学细细阅读。


https://www.fengoutiyan.com/post/15487.html

相关文章:

  • sql解析引擎
  • 用代码建SQL数据库
  • 数据库parse
  • java sql解析器
  • sql语法分析器
  • dbms_sql
  • sqlcode 1
  • sql parser
  • 鏡像模式如何設置在哪,圖片鏡像操作
  • 什么軟件可以把圖片鏡像翻轉,C#圖片處理 解決左右鏡像相反(旋轉圖片)
  • 手機照片鏡像翻轉,C#圖像鏡像
  • 視頻鏡像翻轉軟件,python圖片鏡像翻轉_python中鏡像實現方法
  • 什么軟件可以把圖片鏡像翻轉,利用PS實現圖片的鏡像處理
  • 照片鏡像翻轉app,java實現圖片鏡像翻轉
  • 什么軟件可以把圖片鏡像翻轉,python圖片鏡像翻轉_python圖像處理之鏡像實現方法
  • matlab下載,matlab如何鏡像處理圖片,matlab實現圖像鏡像
  • 圖片鏡像翻轉,MATLAB:鏡像圖片
  • 鏡像翻轉圖片的軟件,圖像處理:實現圖片鏡像(基于python)
  • canvas可畫,JavaScript - canvas - 鏡像圖片
  • 圖片鏡像翻轉,UGUI優化:使用鏡像圖片
  • Codeforces,CodeForces 1253C
  • MySQL下載安裝,Mysql ERROR: 1253 解決方法
  • 勝利大逃亡英雄逃亡方案,HDU - 1253 勝利大逃亡 BFS
  • 大一c語言期末考試試題及答案匯總,電大計算機C語言1253,1253《C語言程序設計》電大期末精彩試題及其問題詳解
  • lu求解線性方程組,P1253 [yLOI2018] 扶蘇的問題 (線段樹)
  • c語言程序設計基礎題庫,1253號C語言程序設計試題,2016年1月試卷號1253C語言程序設計A.pdf
  • 信奧賽一本通官網,【信奧賽一本通】1253:抓住那頭牛(詳細代碼)
  • c語言程序設計1253,1253c語言程序設計a(2010年1月)
  • 勝利大逃亡英雄逃亡方案,BFS——1253 勝利大逃亡
  • 直流電壓測量模塊,IM1253B交直流電能計量模塊(艾銳達光電)
  • c語言程序設計第三版課后答案,【渝粵題庫】國家開放大學2021春1253C語言程序設計答案
  • 18轉換為二進制,1253. 將數字轉換為16進制
  • light-emitting diode,LightOJ-1253 Misere Nim
  • masterroyale魔改版,1253 Dungeon Master
  • codeformer官網中文版,codeforces.1253 B
  • c語言程序設計考研真題及答案,2020C語言程序設計1253,1253計算機科學與技術專業C語言程序設計A科目2020年09月國家開 放大學(中央廣播電視大學)
  • c語言程序設計基礎題庫,1253本科2016c語言程序設計試題,1253電大《C語言程序設計A》試題和答案200901
  • 肇事逃逸車輛無法聯系到車主怎么辦,1253尋找肇事司機