我在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
我怎样才能得到这个结果,请帮忙吗?
全部谢谢
答案 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有多混乱。