`
duanhengbin
  • 浏览: 383423 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

用正则表达式提取SQL

阅读更多

本文记录一个用正则解决问题的过程。较多的是思路的笔记。

 

需求:   

    分析项目中 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#正则表达式提取网页数据

    C#语言实现采用正则表达式提取网页上需要的数据,并将数据存储到数据库sql sever 2005

    SQL 语法分析,正则表达式解析C#文件;正则表达式实现的语法分析引擎

    一些资料关于 SQL 语法分析;用正则表达式解析C#文件;使用正则表达式实现的语法分析引擎(C#源代码) ;SQL Server 2005正则表达式使模式匹配和数据提取变得更容易;

    电话号码及日期时间提取(正则表达式 C)

    电话号码及日期时间提取(采用正则表达式方式的C语言代码) 支持中国大陆区域通用手机号及固定电话号码提取,简体中文文本网页时间提取。

    sql server 提取汉字/数字/字母的方法 demo

    sql server 提取汉字/数字/字母的方法 里面是Sql语句,以及测试代码等,一看就会,而且是现成的代码

    正则表达式去除中括号(符号)及里面包含的内容

    主要介绍了正则表达式去除中括号(符号)及里面包含的内容,文中给大家提到了正则表达式提取括号内内容,需要的朋友可以参考下

    应该如何构造复杂的正则表达式

    文题本来是《如何构造复杂的正则表达式》,但是觉得有些歧义,就感觉正则式本来很简单,我在教人如何将它...构造能够解析合乎SQL语法的查询语句的正则表达式,应该是比较复杂的。可是,对于具体的问题,也可以更简单

    mysql中如何使用正则表达式查询

    基本形式 属性名 regexp ‘匹配方式’ 正则表达式的模式字符 ^ 匹配字符开始的部分 eg1: 从info表name字段中查询以L开头的记录 select * from info where name regexp ‘^L’; eg2: 从info表name字段中查询以aaa开头...

    匹配中文汉字的正则表达式介绍

    主要介绍了匹配中文汉字的正则表达式介绍,本文同时讲解了匹配中文字符的正则和匹配双字节字符的正则,需要的朋友可以参考下

    截取地址中的省份,城市

    有二种方法 1.是建立一个地区的集合(我存在数据库里面)根据地区来截取。...是利用正则表达式来做的。截取“省”字前面的字这样的方法。 这个是在之前一个项目上改的所以是springboot的框架自己看一下。

    SQL Server中利用正则表达式替换字符串的方法

    建立正则替换函数,利用了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脚本抽取数据库表名小工具.zip

    从SQL脚本抽取数据库表名的Python小工具。利用正则表达式实现了从复杂的SQL脚本中抽取出使用的数据库实体表名。

    全国省、市、区、乡镇/街道地址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...

    风越ASP代码生成器 试用版

     批量提取文本,支持正则表达式,可将HTML等文件中指定内容存入数据库 ★  批量查找替换,支持正则表达式  批量文件、文件夹改名,支持正则表达式  正则表达式测试器(可设置、保存常用正则表达式)  获取按键...

    风越ASP代码生成器 [FireAsp Creator]

    12、提供丰富的建站常用VB、JS函数库  13、提供建站常用辅助工具:  屏幕尺  剪切板  常用加解密  批量提取文本,支持正则表达式,可将HTML等文件中指定内容存入数据库 ★  批量查找替换,...

    mysql中取字符串中的数字的语句

    在很多时间我们需要把字符串的数字给取出来,通常大家会用php,asp等这类来操作,本文章介绍了在sql中取字符中的数字办法,有需要的朋友可以参考一下

    SQL_Sever数据库语句大全.zip

    正则表达式提取定义函数 正则条件匹配(结果返回 True 或 False) 字段类型 定义及解释 字段拼接 And逻辑值使用 Between And查询区间数据 Case When判断语句 Cast数值转文本函数 distinct去重 等等

    用正则实现提取代码内容的代码

    我想用javascript正则提取asp代码中 SQL行的 表达式 但是写来写去都不行,各位辛苦帮忙看看! 想提取引号中的SQL表达式 strSql=”Select * from project354 where ID = “&Request(“id”)& and Name=...

Global site tag (gtag.js) - Google Analytics