[英]Finding Functions within SQL
在类似于以下代码的内容中,我只想提取具有多个参数的函数,并希望忽略具有 2 个参数的合并。 请帮助我使用 REGEX 已经为此工作了几天,并认为这可能不是我能够解决的问题。 我相信答案是一个组,并使用子正则表达式或类似的东西解析该组。
AND NOT COALESCE(UPPER(FUNCTION_TO_FIND(B.PARAM, B.TEST_PARAM1, B.TEST_PARAM2, B.TEST_PARAM3,'Routine Type', ATT_TO_DATE())),'NO_VALUE') IN (UPPER('Routine Appointment Letter'))
AND NOT UPPER(DBO.FUNCTION_TO_FIND( B.PARAM , B.TEST_PARAM1 , B.TEST_PARAM2, B.TEST_PARAM3,'Routine Type', ATT_TO_DATE())) IN (UPPER('Routine Appointment Letter'))
AND NOT COALESCE(1, 3) = 2
我希望找到
DBO.FUNCTION_TO_FIND( B.PARAM , B.TEST_PARAM1 , B.TEST_PARAM2, B.TEST_PARAM3,'Routine Type', ATT_TO_DATE())
和
FUNCTION_TO_FIND(B.PARAM, B.TEST_PARAM1, B.TEST_PARAM2, B.TEST_PARAM3,'Routine Type', ATT_TO_DATE())
请注意,这些函数的层数并不总是相同,但它们都会有 2 个以上的参数。
我尝试了以下平衡括号函数的不同版本和编辑,但无法正确计算参数以捕获整个函数。
(\\((?>[^()]|(?1))*\\))
编辑,下面的澄清
EDIT1 :请注意,在查找函数时,我将无法访问将运行 SQL 的服务器,这必须完全脱机完成。
EDIT2 :进一步考虑这个问题,我认为这是一个需要解决的问题,而不是一个正则表达式,而是使用另一种工具即时创建正则表达式。
[[:alnum:]][^)( \\r\\n]+?\\s*?\\(
(\\((?>[^()]|(?1))*\\))
我会尝试一些具有上述效果的东西,然后带着答案回来。 同时,如果有人有不同的想法,请随时贡献。
好吧,我给它一个尝试,并Microsoft.SqlServer.Management.SqlParser.Parser
从Microsoft.SqlServer.SqlManagementObjects
NuGet包可能是要走的路。
var sql = @"SELECT .... ";
var result = Parser.Parse(sql);
var batch = result.Script.Batches.First();
var select = batch.Children.Cast<SqlSelectStatement>().First();
var selectSpec = select.SelectSpecification;
因此, selectSpec.Xml
包含一个表示SELECT
语句的分层 XML 文档。
SqlCodeObject
提供了一个实现访问者模式的Accept()
方法。 编写访问者并评估您感兴趣的类型的表达式: SqlNullScalarExpression
、 SqlUserDefinedScalarFunctionCallExpression
,也许更多。
请注意,解析器无法识别没有dbo.
用户定义函数dbo.
架构:
<SqlNullScalarExpression Location="((3,9),(3,137))">
<!--COALESCE(UPPER(FUNCTION_TO_FIND(B.PARAM, B.TEST_PARAM1, B.TEST_PARAM2, B.TEST_PARAM3,'Routine Type', ATT_TO_DATE())),'NO_VALUE')-->
</SqlNullScalarExpression>
相对
<SqlUserDefinedScalarFunctionCallExpression Location="((4,15),(4,122))" ObjectIdentifier="DBO.FUNCTION_TO_FIND">
<!--DBO.FUNCTION_TO_FIND( B.PARAM , B.TEST_PARAM1 , B.TEST_PARAM2, B.TEST_PARAM3,'Routine Type', ATT_TO_DATE())-->
<SqlObjectIdentifier Location="((4,15),(4,35))" SchemaName="DBO" ObjectName="FUNCTION_TO_FIND">
<!--DBO.FUNCTION_TO_FIND-->
<SqlIdentifier Location="((4,15),(4,18))" Value="DBO">
<!--DBO-->
</SqlIdentifier>
<SqlIdentifier Location="((4,19),(4,35))" Value="FUNCTION_TO_FIND">
<!--FUNCTION_TO_FIND-->
</SqlIdentifier>
</SqlObjectIdentifier>
...
<SqlBuiltinScalarFunctionCallExpression Location="((4,108),(4,121))" FunctionName="ATT_TO_DATE" IsStar="False">
<!--ATT_TO_DATE()-->
</SqlBuiltinScalarFunctionCallExpression>
</SqlUserDefinedScalarFunctionCallExpression>
我在 c# 中使用了以下与正则表达式混合来完成这个任务,是的,它非常粗糙,可以使用一些改进,但它确实有效。 随意改进答案,但我将无法为下面的代码提供持续支持。
Console.WriteLine("=====================================================");
int numberOfIterations = 0;
do
{// Variables
string searchResult; // Start of function call
string functionName; // Function Name
// funcFonud will be char length of function ( "IN (" length is 4)
// used to trim the substring for the next itteration.
int funcFoundNameLen = 0;
int funcFoundAt = 0;
int matchedParenEnd = 0; // End of Matched Paren
// Find the start of a function call.
string findFuncReg = "[a-zA-Z][^)( \\r\\n\\\"]+?\\s*?\\("; // fixes [[:alnum:]] issue
if (query.Length > 0)
{
Match match = Regex.Match(query, findFuncReg);
Console.WriteLine("Trying to match: "+query);
if (match.Success && match.Index >= 0) // Function call found
{
try
{
Console.WriteLine("Match was found: " + match.Index.ToString() + match.Value);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
funcFoundNameLen = match.Length;
funcFoundAt = match.Index;
searchResult = query.Substring(funcFoundAt);
functionName = searchResult.Substring(0, searchResult.IndexOf('(')).Trim();
writeFunc(functionName);
}
else
{
Console.WriteLine("Function Start NOT found");
return;
}
string subString = query.Substring(match.Index);
int openParen = 0;
// Following finds the matched paren
for (int i = 0; i < subString.Length; i++)
{
if (subString[i] == ')' && openParen == 1)
{
matchedParenEnd = i + 1; // Location of the end Paren
break;
}
// Following handles opening and closing of paren so that we
// can find the matched paren.
else
{
if (subString[i] == '(')
{
openParen = openParen + 1;
}
if (subString[i] == ')')
{
openParen = openParen - 1;
}
}
}
// Output function call.
string subCall = subString.Substring(funcFoundNameLen, matchedParenEnd- funcFoundNameLen);
Console.WriteLine("Sub Call: " + subCall);
// Set up for recursive call.
// string querySub = query.Substring(funcFoundAt);
// Console.WriteLine("querySub: " + querySub);
matchedParen(subCall);
// Substring based on the location that the function call was
// made to the end of the function call
Console.WriteLine("Remove: " + (funcFoundAt + matchedParenEnd).ToString() + " letters");
query = query.Substring(funcFoundAt + matchedParenEnd);
numberOfIterations++;
}
} while (query.Length > 0
// && numberOfIterations < 1
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.