【问题标题】:Regular expression to find all table names in a query在查询中查找所有表名的正则表达式
【发布时间】:2008-11-11 14:37:59
【问题描述】:

我对正则表达式并不那么热情,它让我的小脑袋融化了一些东西。

我正在尝试查找查询中的所有表名。所以说我有查询:

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

我想拉出“table1,table2”或“table1”和“table2”

但是如果没有 where 语句怎么办。它可能是文件的结尾,或者可能是 group by 或 order by 等。我知道“大多数”时间这不会是一个问题,但我不喜欢为“大多数”编码的想法情况并知道我留下了一个可能导致以后出现问题的漏洞。

这是一个可行的正则表达式吗?我是正则表达式的普通人吗?

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

【问题讨论】:

  • 正则表达式是您的问题中最少的问题。仅列举表在 SQL 语句中出现的所有方式是一个复杂的问题。顺便提一句。您从未提及您尝试解析哪种类型的 SQL。
  • 也不是他想要解决的根本问题。
  • 我不认为正则表达式是正确的解决方案,你需要一个 SQL Parser,查看这篇文章:dpriver.com/blog/…

标签: .net sql regex


【解决方案1】:

RegEx 并不擅长于此,因为它比看起来要复杂得多:

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

我可以继续。

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

【讨论】:

  • 我认为真正的交易杀手将是视图。将没有实用的方法来解析查询中包含的任何视图的基础表名。
【解决方案2】:

关于这种正则表达式在 SQL 上下文中的有用性的一切都说了。如果您坚持使用正则表达式并且您的 SQL 语句总是看起来像您显示的那样(这意味着没有子查询、连接等),您可以使用

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

【讨论】:

    【解决方案3】:

    我发现这个网站有一个很棒的解析器!

    http://www.sqlparser.com/

    非常值得。工作一种享受。

    【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案5】:

        一种解决方法是对表和视图实施命名约定。然后就可以在命名前缀上解析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")

        【讨论】:

          【解决方案6】:

          这绝对不容易。

          考虑子查询。

          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)
          

          此查询中使用了三个表。

          【讨论】:

            【解决方案7】:

            我认为标记字符串并查找可以绑定表名的 SQL 关键字会更容易。您知道名称将跟随在 FROM 之后,但它们之后可能是 WHEREGROUP BYHAVING,或者如果它们位于查询的末尾,则根本没有关键字。

            【讨论】:

              【解决方案8】:

              构造一个正则表达式将是您最少的问题。根据您希望使用此代码支持的 SQL 风格,您可以在 SQL 语句中引用表的方式数量惊人。

              另外,如果查询包含对视图或 UDF 的引用,则有关哪些基础表的信息甚至根本不会出现在字符串中,这使得通过解析获取该信息完全不切实际。此外,您需要聪明地检测临时表并将其从结果中排除。

              如果您必须这样做,更好的方法是使用 SQL 所针对的特定数据库引擎的 API。例如,您可以基于查询创建一个视图,然后使用 DB Server api 来检测该视图的依赖关系。数据库引擎将能够比以往任何时候都更可靠地解析它,而无需对查询引擎进行大量的逆向工程。

              如果您碰巧使用 SQL Server,这里有一篇关于检测该平台上的依赖关系的文章:Finding Dependencies in SQL Server 2005

              【讨论】:

                【解决方案9】:

                这将在插入查询中提取表名:

                (?<=(INTO)\s)[^\s]*(?=\(())
                

                以下将做同样的事情,但选择包括连接

                (?<=(from|join)\s)[^\s]*(?=\s(on|join|where))
                

                如果您只想返回插入查询中保存的值,最后回到插入,请使用以下正则表达式

                (?i)(?<=VALUES[ ]*\().*(?=\))
                

                我知道这是一个旧线程,但它可能有助于其他人环顾四周

                享受

                【讨论】:

                  【解决方案10】:

                  我尝试了以上所有方法,但都没有奏效,因为我使用了各种各样的查询。虽然我正在使用 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;
                  
                  }
                  

                  【讨论】:

                    【解决方案11】:

                    在 PHP 中,我使用了这个函数,它返回一个包含 sql 语句中使用的表名的数组:

                    function sql_query_get_tables($statement){
                        preg_match_all("/(from|into|update|join) [\\'\\´]?([a-zA-Z0-9_-]+)[\\'\\´]?/i",
                                $statement, $matches);
                        if(!empty($matches)){
                            return array_unique($matches[2]);
                        }else return array();
                    }
                    

                    请注意,它不适用于 a、b 连接或 schema.tablename 命名

                    希望对你有用

                    【讨论】:

                      【解决方案12】:

                      我将此代码用作 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
                      

                      【讨论】:

                        猜你喜欢
                        • 2012-11-17
                        • 1970-01-01
                        • 2013-10-27
                        • 1970-01-01
                        • 2016-09-04
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多