正则表达式解析有限SQL的子句

时间:2014-07-31 02:23:05

标签: c# regex sqlite

如何使用正则表达式解析有限的SQL where子句?

where子句的格式受到限制。它不包含子查询。它仅限于“AND”,“OR”和“()”。

因此,如果我获得了where子句,我想从中提取部分。

where子句的3个例子:

  1. ProjectNumber =?
  2. ProjectNumber =? AND severity = 5
  3. (ProjectNumber =?AND severity = 5)OR DueDate< (DATETIME( 'NOW'))
  4. 例如1,我想要“ProjectNumber =?”

    例如2,我想要“ProjectNumber =?”,“severity = 5”

    例如3,我想要“ProjectNumber =?”,“severity = 5”,“DueDate<(DATETIME('NOW'))”

    “?”表示该值已参数化。

    我知道正则表达式不足以解析完整的SQL where-clause。

    我发现这个“(?< = ^ | \ A |(AND | OR))(?:[^'] |'(?:[^'] |'{2})+')* ?(?=(AND | OR)| $ | \ Z)“但它不适用于3。

    编程语言是C#,数据库是SQLite。

    P.S。我是Regex的基本经验。

    感谢。

    P.S。以下是我目前的C#代码:

    string query = @"(ProjectNumber=? AND severity=5) OR DueDate < (DATETIME('NOW'))";
    string pattern = @"(?<=^|\A|(AND|OR))(?:[^']|'(?:[^']|'{2})+')*?(?=(AND|OR)|$|\Z)";
    MatchCollection matches = Regex.Matches(query, pattern);
    foreach (Match match in matches) 
        Console.WriteLine(match.ToString());
        // currently Console.WriteLine() gives the following:
        // (ProjectNumber=?
        // severity=5)
        //  DueDate < (DATETIME('NOW'))
    

1 个答案:

答案 0 :(得分:7)

通过对SQL WHERE谓词施加的限制,可以创建一个正则表达式,在计算(DATETIME('NOW'))等结构时提取比较表达式。

我将展示和解释的正则表达式要求WHERE谓词在语法上是正确的。如果WHERE谓词有语法错误,则正则表达式可能不匹配或产生垃圾结果。

正则表达式的全部荣耀(增加了空格以增强可读性!):

\w[\w\d]* \s*[<>=]{1,2}\s* ( \?|\w[\w\d]*|(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+ )

虽然它肯定不是一个很长的正则表达式,但它仍然难以阅读和理解。因此,让我们解构这个正则表达式并解释它的几个部分。为此,我们将首先看看我们实际想要从WHERE谓词中提取的内容。

我们想要从WHERE谓词中提取的每个表达式都遵循相同的基本模式:

SomeIdentifierWithoutParantheses =|<=|>=|<> SomeOtherThingWithOrWithoutParentheses

这个(a的高级描述)模式足以理解正则表达式需要匹配什么来从WHERE谓词中提取所需的部分。

我们的正则表达式中的第一部分\w[\w\d]*匹配 SomeIdentifierWithoutParantheses 。这可以是以字母数字字符开头的任何标识符,后跟其他字母数字字符和/或数字。此类标识符的示例为 ProjectNumber My1Ident23

正则表达式\s*[<>=]{1,2}\s*中的第二部分与比较操作数=<=>=<>匹配,包括任何空格在比较运算符之前和之后。 (好吧,它也会像=<那样匹配废话,但是-as在开头说 - 我们假设语法正确的SQL。)

正则表达式的第三部分与比较运算符后面的第二个操作数匹配,这看起来有点笨拙:( \?|\w[\w\d]*|(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+ )。让我们进一步解构正则表达式的这一部分。您可能已经注意到,整个事情是三个备选选项的交替,将在下面解释。

\?显然匹配单个问号(如“ProjectNumber =?”)。 \w[\w\d]*匹配标识符的方式与第一个操作数匹配的方式相同。

(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+包含.NET中RegEx引擎的特色:平衡组。使用具有平衡组的这种构造允许正则表达式匹配包含(嵌套)括号组( - )的操作数,例如 DATETIME('NOW')(DATETIME( 'NOW'))

关于StackOverflow的另一个问题,MartinBüttner对平衡群体做了很好的解释(问题的主题是“What are regular expression Balancing Groups?”)我想指出任何不了解平衡群体的人他的回答(click here to navigate to Martin's answer)。另一个很好的解释可以在CodeProject上找到。

您会注意到没有任何东西可以处理布尔运算符,例如 AND OR 。这不是必需的,因为这些布尔运算符不是正则表达式匹配的模式的一部分。 (还记得上面关于模式的崇高描述吗?)


如何在C#中使用这样的正则表达式从WHERE谓词中提取所需的部分?

首先,请注意我将继续在正则表达式中使用空格以提高可读性。这需要使用 RegexOptions.IgnorePatternWhitespace 或“(?x)”内联选项进行RegEx初始化。在下面的代码中,我使用前者。

要从WHERE谓词中提取所有部分,将使用RegEx.Matches方法,该方法返回Match个对象的集合。每个Match对象代表一个提取的部分。

Regex re = new Regex(
    @"\w[\w\d]* \s*[<>=]{1,2}\s* ( \?|\w[\w\d]*|(\w[\w\d]*)*((?<PR>\()|(?<-PR>\))|[^()])+ )",
    RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled
);


string wherePredicate =
    "(ProjectNumber=? AND severity=5) OR DueDate < (DATETIME('NOW'))";
    // or use any other WHERE predicate string here...

MatchCollection mc = re.Matches(wherePredicate);

if (mc.Count == 0)
    Console.WriteLine("No matches found.");
else
    foreach (Match m in mc)
        Console.WriteLine("\"{0}\"", m.Value);    


您可以借助Regex Storm .NET Regex tester在线试验正则表达式和不同的输入字符串。