本文记录一个用正则解决问题的过程。较多的是思路的笔记。
需求:
分析项目中 PL/SQL的表使用状况。也就是哪个程序对那些表做了增删改查。
【初步分析】
乍一看视乎不难,INSERT/UPDATE/DELETE 语句表名比较好根据位置确定,SELECT查询 FROM 关键字后面的字符串并分析,但是实际上这种方法有很大问题,首先是子查询可能出现在from节中,这种情况几乎没办法用程序分析。其次,from节的结尾判定也有很多种,不易考虑周全。
最终确定一个可行的方案是将完整的SQL语句抽出,再利用sql语法树进行分析。
【用于抽出SQL的正则表达式分析】
DML语句都是以 SELECT/INSERT/UPDATE/DELETE 开始,以“;”结尾,但是子查询的情况比较复杂。
SELECT/INSERT/UPDATE/DELETE语句中都有可能出现子查询,而INSERT/UPDATE/DELETE 中有子查询可以理解为第一个出现的表名为INSERT/UPDATE/DELETE对象表,其他为 子查询对象。
SELECT子查询时由“(”和“)”包裹,如果抽出会造成括号不匹配,需要防止“(”后的SELECT子句被抽出 。
最终得到的 抽出各语句按如下正则式进行抽出。
public enum SqlDmlEnum { SELECT("(?<!\\(\\s?)select(\\s)+.*?;"), INSERT("(\\s)+insert(\\s)+.*?;"), UPDATE("(\\s)+update(\\s)+.*?;"), DELETE("(\\s)+delete(\\s)+.*?;"); private final String strReg; private final Pattern p; SqlDmlEnum(String strIn) { this.strReg = strIn; this.p = Pattern.compile(strReg, Pattern.CASE_INSENSITIVE|Pattern.DOTALL); } public List<String> extractSqls(String sqlFile) { Matcher m = p.matcher(sqlFile); List<String> list = new ArrayList<String>(); while (m.find()) { list.add(m.group(0)); } return list; } }
注:“(?<!\\(\\s?)”左括号后面有可能有空格,本来空格数量是未定的,JAVA的逆序环视不支持不确定长度的匹配是个头痛的问题。所以简便的方法是匹配前读入阶段要将多余的空白字符删除(有多个时只留1个),就可以这样简单的匹配了。
【解析器选择】
sql的解析是最重要的一步,自己写解析树需要较多时间,还容易考虑不周。立马开始在开源项目中筛选,遗憾的是没有发现比较成熟的。最终还是选了商业化的gsp(http://www.sqlparser.com/)。作为开源控不能不说相当遗憾,今后能找到了就换。
测试了一下功能比较强大,多重嵌套的子sql都能比较完美地解析。
唯一发现的可能出问题的限制是 解析的SQL中的注释,需要在预处理时解决掉。
【读入文本的处理(预处理)】
这里也较多地使用了正则,首先为简化后面的处理,将回车,换行符替换为空格。
多余的空白字符删除(有多个时只留1个)
string.replaceAll("\\s+", " ")
最重要的是删除注释部分。
·单行注释有两种:
“--”开头的字符串切除(之前前面的内容保留)
string.replaceFirst("--.*", "")
“REM”开头的字符串,整行忽略
·多行注释的删除。 如 “/*???*/”
string.replaceAll("/\\*(?<!\\*/).*?\\*/", "")
·输出消息命令 prompt,也需要删除,方法与“REM”类似。
public static String readFile(String filePath) throws IOException { InputStream is = new FileInputStream(filePath); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuffer sb = new StringBuffer(); String line = reader.readLine(); while (line != null) { //注释行(以REM开头)及 输出命令(prompt)所在行跳过 if (!(line.matches("(?i)^REM") || line.matches("(?i)^PROMPT"))){ //单行注释删除(注意只删注释部分:"--"之后的字符串) //为简化后面的解析处理,将换行符转为空格 sb.append(line.replaceFirst("--.*", "")).append(" "); } line = reader.readLine(); } reader.close(); is.close(); //使用replaceAll("\\s+", " ") 删除多余的空格 //使用replaceAll("/\\*(?<!\\*/).*?\\*/", "") 删除多行注释 return sb.toString().replaceAll("\\s+", " ").replaceAll("/\\*.*?\\*/", ""); }
【其他】
代码中表名使用变量的部分,在excel报表中标记颜色体现。
个别SQL解析失败的(极少数有的话),将SQL 输出到log中。
IO异常不是关注重点,只做了简单抛出处理。
使用到的第三方jar包:
gsp.jar (90天免费试用期 官网http://www.sqlparser.com/ )
log4j-1.2.17.jar
poi-3.7-20101029.jar
【总结】
很久没这么多使用正则了,导致初期调试费了不少周折,由于使用了多个正则表达式辅助,整个程序比较精简。JAVA的逆序环视对确定长度的限制有时比较麻烦的,如果有可能用变通的做法将目标转换为到容易确定的状态,不失为一个好的解决办法。
相关推荐
C#语言实现采用正则表达式提取网页上需要的数据,并将数据存储到数据库sql sever 2005
一些资料关于 SQL 语法分析;用正则表达式解析C#文件;使用正则表达式实现的语法分析引擎(C#源代码) ;SQL Server 2005正则表达式使模式匹配和数据提取变得更容易;
电话号码及日期时间提取(采用正则表达式方式的C语言代码) 支持中国大陆区域通用手机号及固定电话号码提取,简体中文文本网页时间提取。
sql server 提取汉字/数字/字母的方法 里面是Sql语句,以及测试代码等,一看就会,而且是现成的代码
主要介绍了正则表达式去除中括号(符号)及里面包含的内容,文中给大家提到了正则表达式提取括号内内容,需要的朋友可以参考下
文题本来是《如何构造复杂的正则表达式》,但是觉得有些歧义,就感觉正则式本来很简单,我在教人如何将它...构造能够解析合乎SQL语法的查询语句的正则表达式,应该是比较复杂的。可是,对于具体的问题,也可以更简单
基本形式 属性名 regexp ‘匹配方式’ 正则表达式的模式字符 ^ 匹配字符开始的部分 eg1: 从info表name字段中查询以L开头的记录 select * from info where name regexp ‘^L’; eg2: 从info表name字段中查询以aaa开头...
主要介绍了匹配中文汉字的正则表达式介绍,本文同时讲解了匹配中文字符的正则和匹配双字节字符的正则,需要的朋友可以参考下
有二种方法 1.是建立一个地区的集合(我存在数据库里面)根据地区来截取。...是利用正则表达式来做的。截取“省”字前面的字这样的方法。 这个是在之前一个项目上改的所以是springboot的框架自己看一下。
建立正则替换函数,利用了OLE对象,以下是函数代码: --如果存在则删除原有函数 IF OBJECT_ID(N'dbo.RegexReplace') IS NOT NULL DROP FUNCTION dbo.RegexReplace GO --开始创建正则替换函数 CREATE FUNCTION dbo...
字符串提取(获取两个字符串中间的字符串) http://blog.csdn.net/isaced/archive/2011/01/24/6161259.aspx
从SQL脚本抽取数据库表名的Python小工具。利用正则表达式实现了从复杂的SQL脚本中抽取出使用的数据库实体表名。
建表SQL DROP TABLE IF EXISTS `area`; CREATE TABLE `area` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `parent_id` int(11) NOT NULL COMMENT '父级', `name` varchar(50) NOT NULL DEFAULT '' COMMENT...
批量提取文本,支持正则表达式,可将HTML等文件中指定内容存入数据库 ★ 批量查找替换,支持正则表达式 批量文件、文件夹改名,支持正则表达式 正则表达式测试器(可设置、保存常用正则表达式) 获取按键...
12、提供丰富的建站常用VB、JS函数库 13、提供建站常用辅助工具: 屏幕尺 剪切板 常用加解密 批量提取文本,支持正则表达式,可将HTML等文件中指定内容存入数据库 ★ 批量查找替换,...
在很多时间我们需要把字符串的数字给取出来,通常大家会用php,asp等这类来操作,本文章介绍了在sql中取字符中的数字办法,有需要的朋友可以参考一下
正则表达式提取定义函数 正则条件匹配(结果返回 True 或 False) 字段类型 定义及解释 字段拼接 And逻辑值使用 Between And查询区间数据 Case When判断语句 Cast数值转文本函数 distinct去重 等等
我想用javascript正则提取asp代码中 SQL行的 表达式 但是写来写去都不行,各位辛苦帮忙看看! 想提取引号中的SQL表达式 strSql=”Select * from project354 where ID = “&Request(“id”)& and Name=...