需求:匹配SQL查询区域的正则表达式,拆分区域与对应值
拆分带嵌套子查询的SQL区域关键字内容
要解决这个问题,核心难点是处理括号嵌套的子查询,避免子查询内的同名字段/关键字干扰外层SQL的区域分割。单纯按关键字分割会直接失效,必须结合平衡括号匹配来确保每个区域的内容归属正确。
核心思路
- 用正则的平衡组特性处理嵌套括号,确保子查询内部的内容被完整包含在对应区域中,不会提前触发下一个关键字的匹配。
- 精准匹配指定的区域关键字(作为完整单词,避免字段名包含关键字的误匹配),并关联其对应的内容直到下一个关键字或SQL结尾。
正则表达式(C#环境)
\b(SELECT|FROM|WHERE|GROUP BY|HAVING|ORDER BY|OFFSET|LIMIT)\b\s*(?(?=\()((?>[^()]+|(?<open>\()|(?<-open>\()))*(?(open)(?!))|.*?)(?=\b(SELECT|FROM|WHERE|GROUP BY|HAVING|ORDER BY|OFFSET|LIMIT)\b|$)
正则解析
\b(关键字列表)\b:匹配指定的区域关键字,\b确保是完整单词(比如不会把SELECTION里的SELECT误判为关键字)。\s*:匹配关键字后的任意空白(包括换行、空格)。- 条件分支
(?(?=\()...|.*?):- 如果关键字后紧跟左括号
(,则用平衡组匹配所有嵌套括号内容,直到括号完全闭合。 - 否则,匹配内容直到下一个区域关键字或SQL结尾。
- 如果关键字后紧跟左括号
- 平衡组部分
((?>[^()]+|(?<open>\()|(?<-open>\()))*(?(open)(?!)):通过栈式计数匹配嵌套括号,确保只有当括号完全闭合时才停止匹配。
代码实现(C#)
using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; public class SqlRegionSplitter { public static Dictionary<string, string> SplitSqlIntoRegions(string sql) { var regionKeywords = new[] { "SELECT", "FROM", "WHERE", "GROUP BY", "HAVING", "ORDER BY", "OFFSET", "LIMIT" }; var escapedKeywords = string.Join("|", regionKeywords.Select(Regex.Escape)); var pattern = $@"\b({escapedKeywords})\b\s*(?(?=\()((?>[^()]+|(?<open>\()|(?<-open>\()))*(?(open)(?!))|.*?)(?=\b({escapedKeywords})\b|$)"; var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); var matches = regex.Matches(sql); var regionDict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (Match match in matches) { var regionKey = match.Groups[1].Value.Trim(); var regionValue = match.Groups[2].Success ? match.Groups[2].Value.Trim() : match.Groups[3].Value.Trim(); regionDict[regionKey] = regionValue; } return regionDict; } // 测试示例 public static void Main() { var testSql = @"SELECT id, username FROM (SELECT u.id, u.username FROM users u WHERE u.age >= 18) AS adult_users WHERE adult_users.username LIKE '%dev%' GROUP BY adult_users.id HAVING COUNT(adult_users.id) > 2 ORDER BY adult_users.username DESC OFFSET 5 LIMIT 10"; var result = SplitSqlIntoRegions(testSql); foreach (var kvp in result) { Console.WriteLine($"{kvp.Key}: {kvp.Value}"); } } }
注意事项
- 该实现依赖C#正则的平衡组特性,Python、JavaScript等语言不支持原生平衡组,需改用递归正则或手动编写括号嵌套处理逻辑。
- 启用
IgnoreCase确保兼容SQL大小写不敏感的写法,Singleline模式让.能匹配换行符,支持跨行的SQL语句。 - 若SQL存在注释,需先移除注释再进行拆分,否则注释中的关键字会干扰匹配。
内容的提问来源于stack exchange,提问作者JAlvez
相关产品推荐
相关产品推荐

