Поиск функций в пределах SQL - PullRequest
0 голосов
/ 26 февраля 2020

В чем-то вроде следующего кода я хочу извлечь только функцию, которая имеет более одного параметра, и хочу игнорировать объединение, которое имеет 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())

Обратите внимание, что функции не всегда будут иметь одинаковое количество слоев вниз, но все они будут иметь более двух параметров.

Я пробовал разные версии и правки следующей сбалансированной скобки функции, но он не смог правильно рассчитать параметры для захвата всей функции.

(\((?>[^()]|(?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. ...

Я попробую что-то с вышеприведенным эффектом и вернусь с ответом. В то же время, если у кого-то есть другая идея, пожалуйста, не стесняйтесь вносить свой вклад.

Ответы [ 2 ]

0 голосов
/ 28 февраля 2020

Я использовал следующее в c#, смешанном с регулярным выражением, чтобы выполнить sh эту задачу, да, это очень грубо и может использовать некоторые уточнения, но это работает. Не стесняйтесь улучшать ответ, однако я не смогу обеспечить постоянную поддержку для кода ниже.

            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
        );
0 голосов
/ 26 февраля 2020

Хорошо, я попробовал, и Microsoft.SqlServer.Management.SqlParser.Parser из пакета Microsoft.SqlServer.SqlManagementObjects nuget может быть способом к go.

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 содержит иерархический XML документ, представляющий оператор SELECT.

* SqlCodeObject предоставляет метод Accept(), который реализует шаблон Visitor. Напишите посетителю и оцените выражения интересующих вас типов: 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...