[英]Regular expression to find all table names in a query
我对正则表达式并不热衷,它让我的小脑袋融化了一些东西。
我试图在查询中找到所有表名称。 所以说我有查询:
SELECT one, two, three FROM table1, table2 WHERE X=Y
我想拉出“table1,table2”或“table1”和“table2”
但是,如果没有where声明怎么办? 它可能是文件的结尾,或者可能有一个组或一个顺序等。我知道“大多数”时间这不会是一个问题,但我不喜欢编码“最”的想法情况和知道我留下了一个可能导致事情出错的漏洞。
这是一个可行的正则表达式吗? 我是正则表达式吗?
(PS这将在C#中完成,但假设无关紧要)。
RegEx对此并不是很擅长,因为它比看起来要复杂得多:
我可以继续
你可以做的是寻找一个SQL解析器,并通过它运行您的查询。
所有内容都说明了这种正则表达式在SQL上下文中的用处。 如果你坚持使用正则表达式并且你的SQL语句总是看起来像你展示的那个(这意味着没有子查询,连接等),你可以使用
FROM\s+([^ ,]+)(?:\s*,\s*([^ ,]+))*\s+
我很晚才参加派对,但我想我会分享一个正在使用的正则表达式来分析我们所有的数据库对象,而且我不同意这种情绪,即不可能使用一个这样做。
正则表达式有一些假设
1)您没有使用A,B连接语法样式
2)无论你使用什么正则表达式解析器支持忽略大小写。
3)您正在分析,选择,加入,更新,删除和截断。 它不支持前面提到的MERGE / NATURAL,因为我们不使用它们,但是我相信进一步的支持并不难添加。
我很想知道该表是什么类型的事务,所以我已经包含Named Capture组来告诉我。
现在我已经很久没有使用正则表达式,所以可能会有一些改进,但是在我的所有测试中到目前为止这都是准确的。
\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
一种解决方法是在表和视图上实现命名约定。 然后可以在命名前缀上解析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")
这绝对不容易。
考虑子查询。
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)
此查询中使用了三个表。
我认为将字符串标记化并查找可能绑定表名的SQL关键字会更容易。 您知道名称将遵循FROM
,但如果它们位于查询的末尾,则它们可以跟随WHERE
, GROUP BY
, HAVING
或根本没有关键字。
构建正则表达式将是您遇到的最少问题。 根据您希望使用此代码支持的SQL的风格,您可以在SQL语句中引用表的方式数量是惊人的。
PLUS,如果查询包含对视图或UDF的引用,则有关底层表甚至不在字符串中的信息,通过解析它来获取该信息是完全不切实际的。 此外,您需要聪明地检测临时表并将其从结果中排除。
如果必须这样做,更好的方法是将API用于SQL所针对的特定数据库引擎。 例如,您可以基于查询创建视图,然后使用DB Server api检测该视图的依赖关系。 数据库引擎将能够比以往更加可靠地解析它,而无需对查询引擎进行逆向工程。
如果您偶然使用SQL Server,这里有一篇关于检测该平台上的依赖项的文章: 在SQL Server 2005中查找依赖项
我使用此代码作为Excel宏来解析select和extract表名。
我的解析假定不使用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
这将在insert into查询中提取一个表名:
(?<=(INTO)\s)[^\s]*(?=\(())
以下将执行相同的操作,但选择包括连接
(?<=(from|join)\s)[^\s]*(?=\s(on|join|where))
如果要仅返回插入查询中保存的值,最后返回到插入,请使用以下正则表达式
(?i)(?<=VALUES[ ]*\().*(?=\))
我知道这是一个旧线程,但它可能会帮助其他人环顾四周
请享用
我尝试了以上所有但是没有用,因为我使用了各种各样的查询。 我正在使用PHP并使用一个名为SQL_Parser的PEAR库,但希望我的解决方案有所帮助。 此外,我在使用撇号和MySQL保留sencences时遇到了麻烦,因此我决定在解析之前从查询中删除所有字段部分。
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;
}
在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命名
我希望这个对你有用
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.