Jason Lee
Jason Lee

Reputation: 29

How to parse stored procedure?

I have some complex procedure at MSSQL, single procedure file more than 1000 lines, and with multiple update/insert/delete operation, and I want to parse all of procedure to get operate table object, like as:

alter procedure UP_TestDemo
as
update dbo.Models set A = '1', B = '2' from Modules m where m.ID = '0001'
insert DB.dbo.emplyees( Name ) select Name from Person
 
Exec dbo.UP_LOG @ModifyDate = '05/04/2018'
...
go

Expected analytical results:

How can I get this result, any help please?

Upvotes: 0

Views: 4461

Answers (2)

user1911400
user1911400

Reputation: 1

If I may submit a humble suggestion.

Acquire Textpad.

In SSMS, use the Object Explorer Details to select all your stored procedures, right click and select Script Stored Procedure As ->Create To -> File

Open the file with Textpad.

Use the Mark feature of the Find dialog and Mark all lines with create procedure and EXEC .

Copy all bookmarked lines and past into a new document.

Use the replace feature to search for all EXEC and replace it with \nEXEC. be sure you check the box "Regular Expression".

You now have a list of stored procedures with any Execs indented beneath them. I guess you could use the same for inserts but that depends on how messy your SQL is.

Upvotes: -1

Dan Guzman
Dan Guzman

Reputation: 46241

One method to parse stored procedures is with the Microsoft.SqlServer.TransactSql.ScriptDom. This is used internally for functionality provided by some SQL Server tools.

This parser uses a visitor pattern to interpret the T-SQL abstract syntax tree. The basic C# example below may help you get started for your specific needs. Generic T-SQL parsing is non-trivial due to the breadth and flexibility of the language but you ought to be able to develop a suitable one for your use case.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using System.IO;
public static class ProcParser
{
    static void Main(string[] args)
    {

        var procDef = @"
alter procedure UP_TestDemo
as
update dbo.Models set A = '1', B = '2' from Modules m where m.ID = '0001'
insert DB.dbo.emplyees( Name ) select Name from Person

Exec dbo.UP_LOG @ModifyDate = '05/04/2018'
GO
";
        var statementTargets = ProcParser.GetStatementTargets(procDef);

        foreach(var statementTarget in statementTargets)
        {
            Console.WriteLine(statementTarget);
        }

    }

    public static List<String> GetStatementTargets(string storedProcedureDefinition)
    {

        StringReader reader = new StringReader(storedProcedureDefinition);

        //specify parser for appropriate SQL version
        var parser = new TSql140Parser(true);

        IList<ParseError> errors;
        TSqlFragment sqlFragment = parser.Parse(reader, out errors);

        if (errors.Count > 0)
        {
            throw new Exception("Error parsing stored procedure definition");
        }

        SQLVisitor sqlVisitor = new SQLVisitor();
        sqlFragment.Accept(sqlVisitor);

        return sqlVisitor.StatementTargets;

    }

}

internal class SQLVisitor : TSqlFragmentVisitor
{

    public List<String> StatementTargets = new List<String>();

    public override void ExplicitVisit(AlterProcedureStatement node)
    {
        node.AcceptChildren(this);
    }

    public override void ExplicitVisit(ExecuteStatement node)
    {
        ExecuteSpecification executeSpec = node.ExecuteSpecification;
        ExecutableProcedureReference executableEntity = (ExecutableProcedureReference)executeSpec.ExecutableEntity;
        var tokenText = getTokenText(executableEntity.ProcedureReference);
        StatementTargets.Add($"Execute SP: {tokenText}");
    }
    public override void ExplicitVisit(UpdateStatement node)
    {
        var tokenText = getTokenText(node.UpdateSpecification.Target);
        StatementTargets.Add($"Update Table: {tokenText}");
    }
    public override void ExplicitVisit(InsertStatement node)
    {
        var tokenText = getTokenText(node.InsertSpecification.Target);
        StatementTargets.Add($"Insert Table: {tokenText}");
    }
    public string getTokenText(TSqlFragment frag)
    {
        var sb = new StringBuilder();
        for(int i = frag.FirstTokenIndex; i <= frag.LastTokenIndex; ++i)
        {
            sb.Append(frag.ScriptTokenStream[i].Text);
        }
        return sb.ToString();

    }
}

Output:

Update Table: dbo.Models
Insert Table: DB.dbo.emplyees
Execute SP: dbo.UP_LOG

Upvotes: 3

Related Questions