SQL 服务器 ScriptDom 解析
SQL Server ScriptDom Parsing
与我合作的开发人员团队正在使用 SQL 数据项目来完成我们必须针对现有数据库进行的大量工作。我们已经几周了,出现了一些问题,但总的来说体验还是不错的。
但是,当我们部署到生产环境时,dba 团队拒绝接受 DACPAC 作为部署方法。相反,他们希望看到每个 DML 或 DDL 语句的传统脚本。
目前的想法是在完成的SQL项目和生产环境之间创建一个差异脚本,然后将其解析为单独的脚本。不好我知道。
要解析差异脚本,似乎有两种选择:
- 根据批处理分隔符命令解析脚本,GO。一个相当基本的解决方案,但显示出希望。
- 或者,使用 Microsoft.SqlServer.TransactSql.ScriptDom。这看起来更适合未来,但似乎要复杂得多。
我目前正在试用 ScriptDom,但无法理解它。我当前的(但不仅是)问题如下。
我正在尝试使用 C# 中的 ScriptDOM 解析以下 SQL:
CREATE TABLE dbo.MyTable
(
MyColumn VARCHAR(255)
)
但看不到如何访问 VARCHAR 大小,在本例中为 255。
我使用的代码如下:
TSqlFragment sqlFragment = parser.Parse(textReader, out errors);
SQLVisitor myVisitor = new SQLVisitor();
sqlFragment.Accept(myVisitor);
public override void ExplicitVisit(CreateTableStatement node)
{
// node.SchemaObjectName.Identifiers to access the table name
// node.Definition.ColumnDefinitions to access the column attributes
}
我希望从每个列定义中找到一个长度 属性 或类似的长度。但是,我也暗暗怀疑您可以使用访问者模式来重新解析每个列的定义,我一直在努力解决这个问题。
有什么想法吗?
太好了,你正在使用 ssdt!
当您的 DBA 不想使用 dacpacs 时,处理此问题的最简单方法是使用 sqlpackage.exe.
预生成部署脚本
我的做法是...
- 将 t-sql 代码检查到项目中
- Build server 构建ssdt项目
- 在 ci 服务器上部署和 运行 测试
- 使用 sqlpackage.exe /action:script 将 dacpac 与 QA、PROD 等进行比较并生成部署脚本。
DBA 然后获取该脚本(或者当我们准备好时,我们告诉他们要获取的内部版本号)- 他们可以仔细阅读和部署该脚本。
注意事项:
- 您将需要访问产品数据库 或 您可以使用的镜像副本,您不需要 dbo 或任何东西,只需要 (https://the.agilesql.club/Blogs/Ed-Elliott/What-Permissions-Do-I-Need-To-Generate-A-Deploy-Script-With-SSDT)
- 脚本仅在 prod 数据库中的模式发生变化之前有效 - 因此如果您生成 4 个脚本和 运行 脚本 1,其他三个无效,您将需要重新 运行 生成脚本的构建。
如果您没有 CI 设置,您可以只使用 sqlpackage.exe 生成没有自动位的脚本:)
希望对您有所帮助!
编辑
我认为这里根本不需要访客。如果我正确地理解了您的目标,您希望使用 SSDT 生成的 TSQL,使用 SQLDOM 对其进行解析,然后分别打印这些批次。执行此操作的代码如下所示:
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlServer.TransactSql.ScriptDom;
namespace ScriptDomDemo
{
class Program
{
static void Main(string[] args)
{
TSql120Parser parser = new TSql120Parser(false);
IList<ParseError> errors;
using (StringReader sr = new StringReader(@"create table t1 (c1 int primary key)
GO
create table t2 (c1 int primary key)"))
{
TSqlFragment fragment = parser.Parse(sr, out errors);
IEnumerable<string> batches = GetBatches(fragment);
foreach (var batch in batches)
{
Console.WriteLine(batch);
}
}
}
private static IEnumerable<string> GetBatches(TSqlFragment fragment)
{
Sql120ScriptGenerator sg = new Sql120ScriptGenerator();
TSqlScript script = fragment as TSqlScript;
if (script != null)
{
foreach (var batch in script.Batches)
{
yield return ScriptFragment(sg, batch);
}
}
else
{
// TSqlFragment is a TSqlBatch or a TSqlStatement
yield return ScriptFragment(sg, fragment);
}
}
private static string ScriptFragment(SqlScriptGenerator sg, TSqlFragment fragment)
{
string resultString;
sg.GenerateScript(fragment, out resultString);
return resultString;
}
}
}
至于如何使用这些 AST,我发现最简单的方法是使用 Visual Studio 的调试器来可视化树,因为您可以看到每个节点的实际类型及其所有属性。如您所见,只需少量代码即可解析 TSQL。
#reference Microsoft.SqlServer.BatchParser
#reference Microsoft.SqlServer.BatchParserClient
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Management.Common;
namespace ScriptParser
{
class Program
{
static void Main(string[] args)
{
ExecuteBatch batcher = new ExecuteBatch();
string text = File.ReadAllText("ASqlFile.sql");
StringCollection statements = batcher.GetStatements(text);
foreach (string statement in statements)
{
Console.WriteLine(statement);
}
}
}
}
与我合作的开发人员团队正在使用 SQL 数据项目来完成我们必须针对现有数据库进行的大量工作。我们已经几周了,出现了一些问题,但总的来说体验还是不错的。
但是,当我们部署到生产环境时,dba 团队拒绝接受 DACPAC 作为部署方法。相反,他们希望看到每个 DML 或 DDL 语句的传统脚本。
目前的想法是在完成的SQL项目和生产环境之间创建一个差异脚本,然后将其解析为单独的脚本。不好我知道。
要解析差异脚本,似乎有两种选择:
- 根据批处理分隔符命令解析脚本,GO。一个相当基本的解决方案,但显示出希望。
- 或者,使用 Microsoft.SqlServer.TransactSql.ScriptDom。这看起来更适合未来,但似乎要复杂得多。
我目前正在试用 ScriptDom,但无法理解它。我当前的(但不仅是)问题如下。
我正在尝试使用 C# 中的 ScriptDOM 解析以下 SQL:
CREATE TABLE dbo.MyTable
(
MyColumn VARCHAR(255)
)
但看不到如何访问 VARCHAR 大小,在本例中为 255。
我使用的代码如下:
TSqlFragment sqlFragment = parser.Parse(textReader, out errors);
SQLVisitor myVisitor = new SQLVisitor();
sqlFragment.Accept(myVisitor);
public override void ExplicitVisit(CreateTableStatement node)
{
// node.SchemaObjectName.Identifiers to access the table name
// node.Definition.ColumnDefinitions to access the column attributes
}
我希望从每个列定义中找到一个长度 属性 或类似的长度。但是,我也暗暗怀疑您可以使用访问者模式来重新解析每个列的定义,我一直在努力解决这个问题。 有什么想法吗?
太好了,你正在使用 ssdt!
当您的 DBA 不想使用 dacpacs 时,处理此问题的最简单方法是使用 sqlpackage.exe.
预生成部署脚本我的做法是...
- 将 t-sql 代码检查到项目中
- Build server 构建ssdt项目
- 在 ci 服务器上部署和 运行 测试
- 使用 sqlpackage.exe /action:script 将 dacpac 与 QA、PROD 等进行比较并生成部署脚本。
DBA 然后获取该脚本(或者当我们准备好时,我们告诉他们要获取的内部版本号)- 他们可以仔细阅读和部署该脚本。
注意事项:
- 您将需要访问产品数据库 或 您可以使用的镜像副本,您不需要 dbo 或任何东西,只需要 (https://the.agilesql.club/Blogs/Ed-Elliott/What-Permissions-Do-I-Need-To-Generate-A-Deploy-Script-With-SSDT)
- 脚本仅在 prod 数据库中的模式发生变化之前有效 - 因此如果您生成 4 个脚本和 运行 脚本 1,其他三个无效,您将需要重新 运行 生成脚本的构建。
如果您没有 CI 设置,您可以只使用 sqlpackage.exe 生成没有自动位的脚本:)
希望对您有所帮助!
编辑
我认为这里根本不需要访客。如果我正确地理解了您的目标,您希望使用 SSDT 生成的 TSQL,使用 SQLDOM 对其进行解析,然后分别打印这些批次。执行此操作的代码如下所示:
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlServer.TransactSql.ScriptDom;
namespace ScriptDomDemo
{
class Program
{
static void Main(string[] args)
{
TSql120Parser parser = new TSql120Parser(false);
IList<ParseError> errors;
using (StringReader sr = new StringReader(@"create table t1 (c1 int primary key)
GO
create table t2 (c1 int primary key)"))
{
TSqlFragment fragment = parser.Parse(sr, out errors);
IEnumerable<string> batches = GetBatches(fragment);
foreach (var batch in batches)
{
Console.WriteLine(batch);
}
}
}
private static IEnumerable<string> GetBatches(TSqlFragment fragment)
{
Sql120ScriptGenerator sg = new Sql120ScriptGenerator();
TSqlScript script = fragment as TSqlScript;
if (script != null)
{
foreach (var batch in script.Batches)
{
yield return ScriptFragment(sg, batch);
}
}
else
{
// TSqlFragment is a TSqlBatch or a TSqlStatement
yield return ScriptFragment(sg, fragment);
}
}
private static string ScriptFragment(SqlScriptGenerator sg, TSqlFragment fragment)
{
string resultString;
sg.GenerateScript(fragment, out resultString);
return resultString;
}
}
}
至于如何使用这些 AST,我发现最简单的方法是使用 Visual Studio 的调试器来可视化树,因为您可以看到每个节点的实际类型及其所有属性。如您所见,只需少量代码即可解析 TSQL。
#reference Microsoft.SqlServer.BatchParser
#reference Microsoft.SqlServer.BatchParserClient
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Management.Common;
namespace ScriptParser
{
class Program
{
static void Main(string[] args)
{
ExecuteBatch batcher = new ExecuteBatch();
string text = File.ReadAllText("ASqlFile.sql");
StringCollection statements = batcher.GetStatements(text);
foreach (string statement in statements)
{
Console.WriteLine(statement);
}
}
}
}