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。其代码简洁高效,结构清晰,值得感兴趣的同学细细阅读。

实用SQL代码解析工具——sqlparse相关推荐

  1. 开源 sql 代码提示工具_有关如何计划开源活动的提示

    开源 sql 代码提示工具 在今年的OSCON上 ,Kara Sowles和Francesca Krihely就如何计划和举办技术活动举办了精彩的研讨会. 我参加过的许多技术活动看起来都是完全无缝的, ...

  2. 推荐两个实用的视频解析工具

    01 BTNULL BTNULL是一个视频解析网站,为我们提供了方便快捷的在线视频解析服务.该网站支持解析各种常见的视频网站和平台,我们只需将视频链接粘贴到网站上即可进行解析和观看. BTNULL网站 ...

  3. 【PC工具】大神级代码注释,漂亮实用的代码注释工具代码logo工具

    github上看各路大牛大神的项目代码,经常会看到各种神注释 ...... 那么问题就来了:大神是如何在代码里搞的这些图片代码呢? 打死我也不信是大牛大神一个一个打上去,这不是大牛大神的风格 . 今天 ...

  4. java 代码解析工具_改善 Java 代码质量的工具与方法

    原标题:改善 Java 代码质量的工具与方法 我们可能见过上面的有关代码质量的图片,究竟如何衡量一段代码好坏? 代码质量是什么?为什么它很重要? 作家通过他的著作来讲述了一个清晰的.令人信服的故事.他 ...

  5. 技术分享 | binlog 实用解析工具 my2sql

    作者:赵黎明 爱可生 MySQL DBA 团队成员,Oracle 10g OCM,MySQL 5.7 OCP,擅长数据库性能问题诊断.事务与锁问题的分析等,负责处理客户 MySQL 及我司自研 DMP ...

  6. 在线Javascript代码加密工具:JJEncode

    什么是JJEncode? 就是将JavaScript代码转换成只有符号的字符串编码. 在线JJEncode加密工具:http://www.atoolbox.net/Tool.php?Id=704 温馨 ...

  7. vscode代码运行时间工具_10款实用的VSCode插件提升你的编辑体验 | 第98期

    代码编辑器或者文本编辑器相信大家都不会陌生,但是,常用Windows的朋友大概都知道其自带的"文本编辑器"那是一款多么难用的软件.后来又有一系列的编辑器,比如notepad++.s ...

  8. /plus/recommend.php sql注入漏洞,DedeCMS 全版本通杀SQL注入漏洞利用代码及工具 -

    DedeCMS 全版本通杀SQL注入漏洞利用代码及工具 目前官方最新版已修复该漏洞 V5.7.37 GBK正式版20140228常规更新补丁 http://www.dedecms.com/pl/ ht ...

  9. php解析命令行参数选项,PHP 命令行参数解析工具类的示例代码

    PHP 命令行参数解析工具类的示例代码 /** * 命令行参数解析工具类 * @author guolinchao */ class CommandLine { // 临时记录短选项的选项值 priv ...

  10. Unity 实用代码 小工具

    Unity 实用代码 小工具 Unity 屏幕截图 全屏截图方法 全屏截图方法 带委托事件 自定义截图方法 自定义截图方法 带委托 延迟工具 携程延迟方法 携程延迟带委托方法 场景加载 场景加载 方法 ...

最新文章

  1. 在Yolov5 Yolov4 Yolov3 TensorRT 实现Implementation
  2. webbrowser1 脚本报错_c# winform程序 webBrowser 当前页面的脚本发生异常 找不到成员...
  3. Freemarker静态化ActiveMQ实现
  4. 操作系统之多线程编程—读者优先/写者优先详解
  5. mybatis实现分页查询-自己封装分页方法
  6. 用Python编写博客导出工具
  7. c++调用gcd函数_c++函数库中一些实用的函数
  8. Caffe训练时出现了无数个Train net output #.....
  9. 字符串的最长不重复字串
  10. 【Kafka】kafka 脚本kafka-configs.sh用法解析
  11. 网址路由Routing组件如何在mvc中生成网址
  12. 关于Java里try/catch/finally/有return时执行过程
  13. 免费45天WPS稻壳会员领取
  14. 浙江大学14届计算机学院倩倩,胡倩倩(浙江大学副教授)_百度百科
  15. 对项目需求书撰写的一点理解
  16. reset清除所有浏览器默认样式
  17. Python简单词云的制作
  18. LyX 发布撑持 CJK 的 1.5 正式版
  19. linux下java导出execl_POI实现导出EXCEL详解
  20. 信号完整性入门笔记一-细解为什么低频信号在较短传输线不考虑反射?

热门文章

  1. 神舟IV号开发板-带屏例程源码修改bug(2.8寸屏)
  2. 微型计算机属于超大规模集成电路计算机,超大规模集成电路计算机是第几代计算机...
  3. Radius 协议介绍
  4. c语言数据类型ppt,C语言基本数据类型.ppt
  5. kali2021安装sougou输入法
  6. 机械制图与计算机绘图实训报告前言,机械制图论文2000字_机械制图总结以及心得2000字_大一机械制图结课论文...
  7. php 爬虫图片代码,python爬虫入门教程之糗百图片爬虫代码分享
  8. 提高 Mac OS X 速度的 52 个方法
  9. 攻防世界 --> funny_video --> 最完整和正确的解答
  10. boost::asio 阻塞卡顿问题