在查询中查找所有表名的正则表达式

12

我对正则表达式并不是太熟悉,这让我的小脑袋有些热烧。

我正在尝试在查询中找到所有的表名。假设我有以下查询:

SELECT one, two, three FROM table1, table2 WHERE X=Y

我想要提取"table1, table2"或者"table1"和"table2"

但是如果没有where语句怎么办。可能是文件的结尾,也可能有group by或order by等等。我知道通常情况下这不是问题,但是我不喜欢为"大多数"情况编程,并且知道我留下了可能会在以后引起问题的漏洞。

这个正则表达式是否可行?我是不是太菜了?

(P.S. 这将在C#中完成,但假设这并不重要)。


正则表达式并不是你需要担心的最重要的问题。仅枚举SQL语句中表格出现的所有方式就是一个复杂的问题。顺便提一下,你从未提到你试图解析哪种SQL语言方言。 - JohnFx
他试图解决的根本问题是什么,我们也不清楚。 - Lasse V. Karlsen
我认为正则表达式不是正确的解决方案,你需要使用SQL解析器,可以参考这篇文章:http://www.dpriver.com/blog/list-of-demos-illustrate-how-to-use-general-sql-parser/get-columns-and-tables-in-sql-script-net-version/ - James
12个回答

13

正则表达式在处理这个问题上并不是很好用,因为它比看起来更加复杂:

  • 如果他们使用LEFT/RIGHT INNER/OUTER/CROSS/MERGE/NATURAL连接,而不是a,b语法呢? 无论如何,应该避免使用a,b语法。
  • 嵌套查询怎么办?
  • 如果没有表格(选取常量)怎么办?
  • 换行和其他空格格式怎么办?
  • 别名怎么办?

我可以继续下去。

您可以寻找一个SQL解析器,并将您的查询通过它运行。


我认为真正的问题在于视图。将查询中包含的任何视图的基础表名解析出来,没有实际的方法。 - JohnFx

5

关于在SQL环境中使用这种正则表达式的有用性,人们已经说得很清楚了。如果您坚持使用正则表达式,并且您的SQL语句始终像您展示的那样(也就是没有子查询、连接等),您可以使用以下方法:

FROM\s+([^ ,]+)(?:\s*,\s*([^ ,]+))*\s+ 

4
我来晚了,但我想分享一下我目前正在使用的正则表达式来分析我们所有的数据库对象。我不同意使用一个正则表达式不可能做到这一点的观点。
该正则表达式有几个假设:
1)您没有使用A、B连接语法样式
2)您正在使用的任何正则表达式解析器都支持忽略大小写。
3)您正在分析选择、联接、更新、删除和截断。它不支持上述MERGE/NATURAL,因为我们不使用它们,但我相信进一步支持不难添加。
我很想知道表格是属于哪种类型的交易,因此我包括了命名捕获组来告诉我。
现在我已经很久没有使用正则表达式了,所以可能还有改进的空间,但是在我所有的测试中,这是准确的。
\bjoin\s+(?<Retrieve>[a-zA-Z\._\d]+)\b|\bfrom\s+(?<Retrieve>[a-zA-Z\._\d]+)\b|\bupdate\s+(?<Update>[a-zA-Z\._\d]+)\b|\binsert\s+(?:\binto\b)?\s+(?<Insert>[a-zA-Z\._\d]+)\b|\btruncate\s+table\s+(?<Delete>[a-zA-Z\._\d]+)\b|\bdelete\s+(?:\bfrom\b)?\s+(?<Delete>[a-zA-Z\._\d]+)\b

干得好,先生。接受我的点赞,祝您有个愉快的一天。我甚至不想知道创造那个正则表达式花了多长时间 >:] - undefined

4
我发现了一个非常好用的解析器网站!
http://www.sqlparser.com/
非常值得一试,使用起来非常顺畅。

2

一个解决方法是在表和视图上实现命名约定。然后可以根据命名前缀解析SQL语句。

例如:

SELECT tbltable1.one, tbltable1.two, tbltable2.three
FROM tbltable1
    INNER JOIN  tbltable2
        ON tbltable1.one = tbltable2.three

将空格分隔为数组:

("SELECT", "tbltable1.one,", "tbltable1.two,", "tbltable2.three", "FROM", "tbltable1", "INNER", "JOIN", "tbltable2", "ON", "tbltable1.one", "=", "tbltable2.three")

获取元素左侧的内容直到句点:

("SELECT", "tbltable1", "tbltable1", "tbltable2", "FROM", "tbltable1", "INNER", "JOIN", "tbltable2", "ON", "tbltable1", "=", "tbltable2")

移除带有符号的元素:

("SELECT", "tbltable1", "tbltable1", "tbltable2", "FROM", "tbltable1", "INNER", "JOIN", "tbltable2", "ON", "tbltable1", "tbltable2")

减少到唯一值:

("SELECT", "tbltable1", "tbltable2", "FROM", "INNER", "JOIN", "ON")

筛选左侧3个字符为"tbl"的内容:

("tbltable1", "tbltable2")


1

构建正则表达式将不会是你面临的最小问题。根据你希望使用此代码支持的SQL版本,你可以在SQL语句中引用表的方式有很多种。

此外,如果查询包括对视图或UDF的引用,则关于底层表的信息甚至不会出现在字符串中,因此通过解析字符串来获取该信息完全是不切实际的。此外,你需要聪明地检测临时表并将其从结果中排除。

如果你必须这样做,更好的方法是利用特定数据库引擎的API。例如,你可以基于查询创建一个视图,然后使用DB服务器API来检测该视图的依赖项。与你试图反向工程查询引擎相比,DB引擎能够更可靠地解析它,而你需要付出巨大的努力。

如果你恰好正在使用SQL Server,请参阅以下文章,了解如何在该平台上检测依赖项:Finding Dependencies in SQL Server 2005


1

我认为更容易的方法是对字符串进行分词,并查找可能限定表名的SQL关键字。你知道这些名称将跟随FROM,但它们可能会被WHEREGROUP BYHAVING或者没有关键字跟随,如果它们在查询的末尾。


1

这绝对不容易。

考虑子查询。

select
  *
from
  A
  join (
    select
       top 5 *
    from
      B)
    on B.ID = A.ID
where
  A.ID in (
    select
      ID
    from
      C
    where C.DOB = A.DOB)

这个查询中使用了三个表。


0

我尝试了上述所有方法,但由于我使用了各种查询,所以都没有起作用。虽然我正在使用PHP,并使用了一个名为SQL_Parser的PEAR库,但希望我的解决方案能够帮到你。此外,我在处理撇号和MySQL保留字时遇到了麻烦,因此我决定在解析查询之前将所有字段部分从查询中剥离。

function getQueryTable ($query) {
    require_once "SQL/Parser.php";
    $parser = new SQL_Parser();
    $parser->setDialect('MySQL');

    // Stripping fields section
    $queryType = substr(strtoupper($query),0,6);            
    if($queryType == 'SELECT') { $query  = "SELECT * ".stristr($query, "FROM"); }
    if ($havingPos = stripos($query, 'HAVING')) { $query = substr($query, 0, $havingPos); }


    $struct = $parser->parse($query);

    $tableReferences = $struct[0]['from']['table_references']['table_factors'];

    foreach ((Array) $tableReferences as $ref) {
        $tables[] = ($ref['database'] ? $ref['database'].'.' : $ref['database']).$ref['table'];
    }

    return $tables;

}

0

我将这段代码用作Excel宏来解析选择和提取表名。

我的解析假设不使用语法select from a, b, c

只需对您的SQL查询运行它,如果您对结果不满意,那么您离期望的结果只有几行代码。只需调试并相应地修改代码即可。

Sub get_tables()
    sql_query = Cells(5, 1).Value
    tables = ""

    'get all tables after from
    sql_from = sql_query

    While InStr(1, UCase(sql_from), UCase("from")) > 0

        i = InStr(1, UCase(sql_from), UCase("from"))
        sql_from = Mid(sql_from, i + 5, Len(sql_from) - i - 5)
        i = InStr(1, UCase(sql_from), UCase(" "))

        While i = 1

            sql_from = Mid(sql_from, 2, Len(sql_from) - 1)
            i = InStr(1, UCase(sql_from), UCase(" "))

        end

        i = InStr(1, sql_join, Chr(9))

        While i = 1

            sql_join = Mid(sql_join, 2, Len(sql_join) - 1)
            i = InStr(1, sql_join, Chr(9))

        end

        a = InStr(1, UCase(sql_from), UCase(" "))
        b = InStr(1, sql_from, Chr(10))
        c = InStr(1, sql_from, Chr(13))
        d = InStr(1, sql_from, Chr(9))

        MinC = a

        If MinC > b And b > 0 Then MinC = b
        If MinC > c And c > 0 Then MinC = c
        If MinC > d And d > 0 Then MinC = d

        tables = tables + "[" + Mid(sql_from, 1, MinC - 1) + "]"

    end

    'get all tables after join
    sql_join = sql_query

    While InStr(1, UCase(sql_join), UCase("join")) > 0

        i = InStr(1, UCase(sql_join), UCase("join"))
        sql_join = Mid(sql_join, i + 5, Len(sql_join) - i - 5)
        i = InStr(1, UCase(sql_join), UCase(" "))

        While i = 1

            sql_join = Mid(sql_join, 2, Len(sql_join) - 1)
            i = InStr(1, UCase(sql_join), UCase(" "))

        end

        i = InStr(1, sql_join, Chr(9))

        While i = 1

            sql_join = Mid(sql_join, 2, Len(sql_join) - 1)
            i = InStr(1, sql_join, Chr(9))

        end

        a = InStr(1, UCase(sql_join), UCase(" "))
        b = InStr(1, sql_join, Chr(10))
        c = InStr(1, sql_join, Chr(13))
        d = InStr(1, sql_join, Chr(9))

        MinC = a

        If MinC > b And b > 0 Then MinC = b
        If MinC > c And c > 0 Then MinC = c
        If MinC > d And d > 0 Then MinC = d

        tables = tables + "[" + Mid(sql_join, 1, MinC - 1) + "]"

    end

    tables = Replace(tables, ")", "")
    tables = Replace(tables, "(", "")
    tables = Replace(tables, " ", "")
    tables = Replace(tables, Chr(10), "")
    tables = Replace(tables, Chr(13), "")
    tables = Replace(tables, Chr(9), "")
    tables = Replace(tables, "[]", "")

End Sub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接