如何解析存储过程?

时间:2018-05-04 09:36:51

标签: c# python sql-server

我在MSSQL上有一些复杂的程序,单个程序文件超过1000行,并且有多个更新/插入/删除操作,我想解析所有程序来获取操作表对象,如:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>

预期的分析结果:   - 更新表:dbo.Models
  - 插入表:DB.dbo.emplyees
  - 执行SP:dbo.UP_LOG

我怎样才能得到这个结果,请帮忙吗?

全部谢谢

2 个答案:

答案 0 :(得分:0)

解析存储过程的一种方法是使用Microsoft.SqlServer.TransactSql.ScriptDom。这在内部用于某些SQL Server工具提供的功能。

此解析器使用visitor pattern来解释T-SQL抽象语法树。下面的基本C#示例可以帮助您开始满足您的特定需求。由于语言的广泛性和灵活性,通用T-SQL解析非常简单,但您应该能够为您的用例开发合适的解析。

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();

    }
}

输出:

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

答案 1 :(得分:0)

如果我可以提出一个卑微的建议。

获取文本板。

在SSMS中,使用“对象资源管理器详细信息”选择所有存储过程,右键单击并选择“脚本存储过程为->创建至->文件

使用Textpad打开文件。

使用“查找”对话框的“标记”功能,并使用创建过程 EXEC 标记所有行。

将所有带有书签的行和过去的行复制到新文档中。

使用替换功能搜索所有EXEC并将其替换为\ nEXEC。确保选中“正则表达式”框。

您现在有了一个存储过程的列表,其下方缩进了所有Exec。我猜您可以将相同的内容用于插入,但这取决于您的SQL有多混乱。