首页 > 解决方案 > 在 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:进一步考虑这一点,我认为这是一个需要解决的问题,而不是一个正则表达式,而是使用另一种工具即时创建正则表达式。

  1. 创建一个我不想找到的常见函数列表TO_CHAR, TO_NUMBER, UPPER, LOWER, COALESCE, MIN, MAX, AND, EXISTS, COALESCE, SUM, FROM
  2. 使用类似以下的内容查找查询字符串中使用的任何函数的开头。 [[:alnum:]][^)( \r\n]+?\s*?\(
  3. 备份字符串中的一个字符并使用以下代码查找匹配的括号。 (\((?>[^()]|(?1))*\))
  4. ...

我将尝试达到上述效果并返回答案。同时,如果有人有不同的想法,请随时贡献。

标签: sqlsql-serverregex

解决方案


好的,我试了一下,Microsoft.SqlServer.Management.SqlParser.ParserMicrosoft.SqlServer.SqlManagementObjectsnuget 包中可能是要走的路。

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 文档。

SqlCodeObjects 提供了一个Accept()实现访问者模式的方法。写一个访问者并评估您感兴趣的类型的表达式:SqlNullScalarExpression, SqlUserDefinedScalarFunctionCallExpression, 可能更多。

请注意,解析器无法识别没有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>

推荐阅读