如何用正则捕获含0个或多个UNION的单条SQL语句中的多个SELECT查询
嘿,我懂你遇到的问题了——想用正则把带UNION的多段SELECT语句拆分开,结果原来的正则因为贪婪匹配把整个语句当成了一个,确实头疼。咱们来一步步搞定这个:
问题根源:贪婪匹配的坑
你原来用的SELECT[\s]*.*from[\s]*里,.*是贪婪模式,它会尽可能多地匹配字符,从第一个SELECT开始,一直吃到最后一个from才停下,自然就把整个多UNION的语句当成了一个匹配项。
解决方案:非贪婪匹配+正向断言
针对你的场景(简单的UNION分隔、无复杂嵌套的SELECT),可以用非贪婪匹配结合正向断言来精准捕获每一段SELECT:
基础版正则(适配UNION)
SELECT\s.*?(?=\sUNION\s|$)
增强版正则(适配UNION/UNION ALL)
如果你的SQL里可能出现UNION ALL,可以调整成:
SELECT\s.*?(?=\sUNION(?:\sALL)?\s|$)
正则各部分解释
SELECT\s:匹配SELECT关键字,后面跟至少一个空格(避免误匹配类似SELECTION这类包含SELECT的单词).*?:非贪婪模式的任意字符匹配,会尽可能早地停止,而不是一直吃到最后(?=\sUNION(?:\sALL)?\s|$):正向零宽断言,用来判断停止条件:- 要么后面是
UNION或UNION ALL(注意前后的空格,避免匹配到字段名里的UNION) - 要么是字符串的结尾
- 要么后面是
测试示例(以Python为例)
用你的SQL语句来测试:
import re sql_stmt = "SELECT A,B,C from ABC Where Z=5 and T=10 UNION SELECT * from GHI Where i=100 UNION SELECT X,Y from XYZ" # 用增强版正则 pattern = r"SELECT\s.*?(?=\sUNION(?:\sALL)?\s|$)" # 加上re.DOTALL处理可能的换行 matches = re.findall(pattern, sql_stmt, re.DOTALL) for num, stmt in enumerate(matches, 1): print(f"第{num}段SELECT语句:") print(stmt.strip()) print("---")
输出结果会是:
第1段SELECT语句: SELECT A,B,C from ABC Where Z=5 and T=10 --- 第2段SELECT语句: SELECT * from GHI Where i=100 --- 第3段SELECT语句: SELECT X,Y from XYZ ---
注意事项:复杂SQL的局限性
如果你的SQL包含嵌套子查询、括号内的UNION这类复杂结构,正则就不太靠谱了——毕竟SQL的语法规则太复杂,正则无法处理嵌套的层级关系。这种情况下,建议用专门的SQL解析库,比如Python的sqlparse、Java的JSqlParser,它们能正确解析SQL的语法结构,比正则更可靠。
内容的提问来源于stack exchange,提问作者Choo William
相关产品推荐
相关产品推荐

