C#:SMO 问题与依赖关系/Multipe Create Table 语句
C#: SMO problems with dependencies / Multipe Create Table statements
我正在尝试使用 SMO 从现有数据库生成 SQL 脚本。
后面会用到这个脚本来恢复没有数据的数据库。
为此,我设置了一个如下所示的方法:
public void GenerateSQLScripts(string dbName)
{
StringBuilder sb = new StringBuilder();
Server server = new Server(SqlServer);
Database db = server.Databases[dbName];
var scriptopt = new ScriptingOptions();
scriptopt.TargetServerVersion = SqlServerVersion.Version105; // Windows 2008 R2
scriptopt.AnsiPadding = true;
scriptopt.WithDependencies = true;
scriptopt.IncludeHeaders = true;
scriptopt.SchemaQualify = true;
scriptopt.ExtendedProperties = true;
scriptopt.TargetDatabaseEngineType = DatabaseEngineType.Standalone;
scriptopt.IncludeDatabaseContext = true;
scriptopt.ScriptDrops = false;
scriptopt.ScriptData = false;
scriptopt.ScriptSchema = true;
scriptopt.DriAllConstraints = true;
scriptopt.DriForeignKeys = true;
scriptopt.Indexes = true;
scriptopt.DriPrimaryKey = true;
scriptopt.DriUniqueKeys = true;
scriptopt.DriChecks = true;
scriptopt.AllowSystemObjects = false;
scriptopt.AppendToFile = false;
scriptopt.ScriptBatchTerminator = true;
// script Tables
foreach (Table t in db.Tables)
{
if (!t.IsSystemObject)
{
StringCollection sc = t.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
//Script Stored Procedures
foreach (StoredProcedure sp in db.StoredProcedures)
{
if (!sp.IsSystemObject)
{
var sc = sp.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
//Views
foreach(View v in db.Views){
if (!v.IsSystemObject)
{
StringCollection sc = v.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
File.WriteAllText(Path, sb.ToString());
}
此方法创建一个 SQL 文件,其中包含针对相同 table 的多个 Create Table
语句。每次引用 table 时都会发生这种情况,无论它是否存在。
我通过关闭 WithDependencies 找到了解决此问题的方法:
scriptopt.WithDependencies = false;
现在脚本小了很多,因为没有额外的语句。但是由于我的语句中没有顺序,一些 table 引用了当时不存在的其他 table。
经过一些研究,我发现 class DependencyWalker.
我更改了我的代码以使用此 class 并对语句的顺序进行排序以恢复我的数据库:
UrnCollection ucol = new UrnCollection();
foreach(Table t in db.Tables){
ucol.Add(t.Urn);
}
DependencyWalker dw = new DependencyWalker(server);
DependencyTree dtree = dw.DiscoverDependencies(ucol.ToArray(), DependencyType.Parents);
DependencyCollection dcol = dw.WalkDependencies(dtree);
// ScriptingOptions
Scripter scripter = new Scripter(server);
foreach (Table t in db.Tables)
{
if (!t.IsSystemObject)
{
scripter.Options = scriptopt;
scripter.Options.WithDependencies = false;
IEnumerable<string> tscripts = scripter.EnumScriptWithList(dcol);
foreach (string s in tscripts)
{
sb.AppendLine(s);
}
}
}
出于测试目的,源代码的其余部分不受影响。
现在我得到了和以前一样的错误。我的脚本充满了多个 Create Table
语句,即使该选项已关闭,也会导致 Table already exists
错误。
Smo 是一桶笑声,不是吗?您会发现依赖遍历器对每个 table...产生了明显冗余的引用...因为多重关系...和级联关系。
帮自己一个忙,输出依赖步行者树的结果(自己步行)...所以你可以看到这是真的。这是我对 "see" 我所得到的所做的。请注意,缩进说明 tables 被多次提及......当你调用 WalkDependencies 时,这些只会被压扁成 "linear list"......这对你没有任何好处。
class Program
{
static void Main( string[ ] args )
{
using ( var connection = new SqlConnection( "Data Source=.;Initial Catalog=...;Integrated Security=True" ) )
{
connection.Open( );
var serverConnection = new ServerConnection( connection );
var server = new Server( serverConnection );
var db = server.Databases[ "..." ];
var objects = new UrnCollection( );
foreach ( Table table in db.Tables )
{
objects.Add( table.Urn );
}
var dependency = new DependencyWalker( server );
var tree = dependency.DiscoverDependencies( objects, DependencyType.Parents );
Walk( tree.FirstChild );
}
}
static void Walk( DependencyTreeNode node, int depth = 0 )
{
Print( node.Urn, depth );
if ( node.HasChildNodes )
{
Walk( node.FirstChild, depth + 1 );
}
if ( node.NextSibling != null )
{
Walk( node.NextSibling, depth );
}
}
static void Print( string message, int depth )
{
var space = string.Empty;
for ( int i = 0; i < depth; i++ ) space += " ";
Debug.WriteLine( string.Format( "{0}{1}", space, message ) );
}
}
我 运行 我在一个小型规范化数据库上使用上述代码,其中包含一组名义上的外键。正是这些外键导致多次提及给定的 table.
这是部分编辑的输出:
Server[@Name='...']/Database[@Name='...']/Table[@Name='AddressTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationComponents' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetApplicationElementExtension' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetApplicationElementName' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetArchitecture' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetArchitectureName' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetSetting' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Settings' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DataFiles' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='FileSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DirectoryEntries' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Names' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='NameSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SecurityIds' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SecuritySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='DirectoryEntrySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Images' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Snapshots' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SnapshotSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='KeyTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ListItems' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Streams' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DataFiles' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='FileSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='MachineAddresses' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='AddressTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='MachineKeys' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='MachineKeySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='KeyTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Names' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='NameSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SecurityIds' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SecuritySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Settings' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SnapshotDirectoryEntries' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Snapshots' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SnapshotSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DirectoryEntries' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Names' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='NameSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SecurityIds' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SecuritySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='DirectoryEntrySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Snapshots' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SnapshotSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Streams' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DataFiles' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='FileSequence' and @Schema='dbo']
快速浏览会发现一些 table 被引用了六七次。
情况在好转之前会变得更糟。您最终要做的是找出独立的东西……可以首先编写脚本的东西。这些东西没有依赖关系。然后,将这些独立项目放入列表中,然后 运行 依赖遍历器一遍又一遍地向列表中添加内容……其中项目仅依赖于您已添加的项目。因此,您将获得 s scriptable 订单。
请注意,就地数据库在技术上仍然无法通过这种方式解决。 Table a 可以引用 table b,后者引用 table a。这是完全合法的(即使不受欢迎)。
我更喜欢做的是使用Visual Studio来完成艰苦的工作:
- 为 Visual Studio 添加 SQL 服务器数据工具,来自 here
- 创建数据库项目。
- 导入您现有的数据库。
- Build+Publish菜单生成脚本
它可以做一些神奇的事情...比如创建对象然后修改它们以处理 a->b->a 类型的事情。您还可以发布到现有数据库,它会仔细生成一个仅进行更改的脚本。厉害了。
经过一些研究,我找到了解决问题的方法。
起初我将 DriForeignKeys
的值更改为 false
。
这导致外键暂时被排除在脚本之外。创建表的脚本时,我使用 ForeignKeyCollection:
提取它们的外键
List<ForeignKeyCollection> fkcolList = new List<ForeignKeyCollection>();
foreach (Table t in db.Tables)
{
fkcolList.Add(t.ForeignKeys); // Extract the foreign keys
if (!t.IsSystemObject)
{
StringCollection sc = t.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
现在我可以单独编写外键脚本并将其附加在脚本末尾:
foreach (ForeignKeyCollection fkcol in fkcolList) // Generate Relations
{
foreach (ForeignKey fk in fkcol)
{
StringCollection sc = fk.Script();
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
fkcolList.Clear();
这可能不是最好的解决方案,因为连续三个循环,但对我来说它工作得很好。
您可能不需要手动遍历任何内容。只需将脚本程序配置为包括您想要的类型(表、存储过程、所有者,甚至数据)和 运行 它将处理依赖项(至少它为我生成了一个 DataOnly 脚本):
var server = new Server(@".\SQLExpress");
var database = server.Databases["mydb"];
var scripter = new Scripter(server);
//scripter.Options.WithDependencies = true; //didn't even need this option
scripter.Options.ScriptData = true;
scripter.Options.ScriptSchema = false;
var tables = database.Tables.Cast<Table>().Where(t => !t.IsSystemObject).ToList();
var scripts = scripter.EnumScriptWithList(tables.Select(t => t.Urn).ToArray());
return string.Join("\n", scripts.Select(s => s));
我正在尝试使用 SMO 从现有数据库生成 SQL 脚本。 后面会用到这个脚本来恢复没有数据的数据库。 为此,我设置了一个如下所示的方法:
public void GenerateSQLScripts(string dbName)
{
StringBuilder sb = new StringBuilder();
Server server = new Server(SqlServer);
Database db = server.Databases[dbName];
var scriptopt = new ScriptingOptions();
scriptopt.TargetServerVersion = SqlServerVersion.Version105; // Windows 2008 R2
scriptopt.AnsiPadding = true;
scriptopt.WithDependencies = true;
scriptopt.IncludeHeaders = true;
scriptopt.SchemaQualify = true;
scriptopt.ExtendedProperties = true;
scriptopt.TargetDatabaseEngineType = DatabaseEngineType.Standalone;
scriptopt.IncludeDatabaseContext = true;
scriptopt.ScriptDrops = false;
scriptopt.ScriptData = false;
scriptopt.ScriptSchema = true;
scriptopt.DriAllConstraints = true;
scriptopt.DriForeignKeys = true;
scriptopt.Indexes = true;
scriptopt.DriPrimaryKey = true;
scriptopt.DriUniqueKeys = true;
scriptopt.DriChecks = true;
scriptopt.AllowSystemObjects = false;
scriptopt.AppendToFile = false;
scriptopt.ScriptBatchTerminator = true;
// script Tables
foreach (Table t in db.Tables)
{
if (!t.IsSystemObject)
{
StringCollection sc = t.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
//Script Stored Procedures
foreach (StoredProcedure sp in db.StoredProcedures)
{
if (!sp.IsSystemObject)
{
var sc = sp.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
//Views
foreach(View v in db.Views){
if (!v.IsSystemObject)
{
StringCollection sc = v.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
File.WriteAllText(Path, sb.ToString());
}
此方法创建一个 SQL 文件,其中包含针对相同 table 的多个 Create Table
语句。每次引用 table 时都会发生这种情况,无论它是否存在。
我通过关闭 WithDependencies 找到了解决此问题的方法:
scriptopt.WithDependencies = false;
现在脚本小了很多,因为没有额外的语句。但是由于我的语句中没有顺序,一些 table 引用了当时不存在的其他 table。 经过一些研究,我发现 class DependencyWalker.
我更改了我的代码以使用此 class 并对语句的顺序进行排序以恢复我的数据库:
UrnCollection ucol = new UrnCollection();
foreach(Table t in db.Tables){
ucol.Add(t.Urn);
}
DependencyWalker dw = new DependencyWalker(server);
DependencyTree dtree = dw.DiscoverDependencies(ucol.ToArray(), DependencyType.Parents);
DependencyCollection dcol = dw.WalkDependencies(dtree);
// ScriptingOptions
Scripter scripter = new Scripter(server);
foreach (Table t in db.Tables)
{
if (!t.IsSystemObject)
{
scripter.Options = scriptopt;
scripter.Options.WithDependencies = false;
IEnumerable<string> tscripts = scripter.EnumScriptWithList(dcol);
foreach (string s in tscripts)
{
sb.AppendLine(s);
}
}
}
出于测试目的,源代码的其余部分不受影响。
现在我得到了和以前一样的错误。我的脚本充满了多个 Create Table
语句,即使该选项已关闭,也会导致 Table already exists
错误。
Smo 是一桶笑声,不是吗?您会发现依赖遍历器对每个 table...产生了明显冗余的引用...因为多重关系...和级联关系。
帮自己一个忙,输出依赖步行者树的结果(自己步行)...所以你可以看到这是真的。这是我对 "see" 我所得到的所做的。请注意,缩进说明 tables 被多次提及......当你调用 WalkDependencies 时,这些只会被压扁成 "linear list"......这对你没有任何好处。
class Program
{
static void Main( string[ ] args )
{
using ( var connection = new SqlConnection( "Data Source=.;Initial Catalog=...;Integrated Security=True" ) )
{
connection.Open( );
var serverConnection = new ServerConnection( connection );
var server = new Server( serverConnection );
var db = server.Databases[ "..." ];
var objects = new UrnCollection( );
foreach ( Table table in db.Tables )
{
objects.Add( table.Urn );
}
var dependency = new DependencyWalker( server );
var tree = dependency.DiscoverDependencies( objects, DependencyType.Parents );
Walk( tree.FirstChild );
}
}
static void Walk( DependencyTreeNode node, int depth = 0 )
{
Print( node.Urn, depth );
if ( node.HasChildNodes )
{
Walk( node.FirstChild, depth + 1 );
}
if ( node.NextSibling != null )
{
Walk( node.NextSibling, depth );
}
}
static void Print( string message, int depth )
{
var space = string.Empty;
for ( int i = 0; i < depth; i++ ) space += " ";
Debug.WriteLine( string.Format( "{0}{1}", space, message ) );
}
}
我 运行 我在一个小型规范化数据库上使用上述代码,其中包含一组名义上的外键。正是这些外键导致多次提及给定的 table.
这是部分编辑的输出:
Server[@Name='...']/Database[@Name='...']/Table[@Name='AddressTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationComponents' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetApplicationElementExtension' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetApplicationElementName' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetArchitecture' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetArchitectureName' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/UserDefinedFunction[@Name='GetSetting' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Settings' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ApplicationElements' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DataFiles' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='FileSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DirectoryEntries' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Names' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='NameSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SecurityIds' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SecuritySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='DirectoryEntrySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Images' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Snapshots' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SnapshotSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='KeyTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='ListItems' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Streams' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DataFiles' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='FileSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='MachineAddresses' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='AddressTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='MachineKeys' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='MachineKeySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='KeyTypes' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Names' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='NameSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SecurityIds' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SecuritySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Settings' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SnapshotDirectoryEntries' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Snapshots' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SnapshotSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DirectoryEntries' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Lists' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='ListSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Names' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='NameSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='SecurityIds' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SecuritySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='DirectoryEntrySequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Snapshots' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Machines' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Architectures' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='SnapshotSequence' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Progress' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='Streams' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Table[@Name='DataFiles' and @Schema='dbo']
Server[@Name='...']/Database[@Name='...']/Sequence[@Name='FileSequence' and @Schema='dbo']
快速浏览会发现一些 table 被引用了六七次。
情况在好转之前会变得更糟。您最终要做的是找出独立的东西……可以首先编写脚本的东西。这些东西没有依赖关系。然后,将这些独立项目放入列表中,然后 运行 依赖遍历器一遍又一遍地向列表中添加内容……其中项目仅依赖于您已添加的项目。因此,您将获得 s scriptable 订单。
请注意,就地数据库在技术上仍然无法通过这种方式解决。 Table a 可以引用 table b,后者引用 table a。这是完全合法的(即使不受欢迎)。
我更喜欢做的是使用Visual Studio来完成艰苦的工作:
- 为 Visual Studio 添加 SQL 服务器数据工具,来自 here
- 创建数据库项目。
- 导入您现有的数据库。
- Build+Publish菜单生成脚本
它可以做一些神奇的事情...比如创建对象然后修改它们以处理 a->b->a 类型的事情。您还可以发布到现有数据库,它会仔细生成一个仅进行更改的脚本。厉害了。
经过一些研究,我找到了解决问题的方法。
起初我将 DriForeignKeys
的值更改为 false
。
这导致外键暂时被排除在脚本之外。创建表的脚本时,我使用 ForeignKeyCollection:
List<ForeignKeyCollection> fkcolList = new List<ForeignKeyCollection>();
foreach (Table t in db.Tables)
{
fkcolList.Add(t.ForeignKeys); // Extract the foreign keys
if (!t.IsSystemObject)
{
StringCollection sc = t.Script(scriptopt);
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
现在我可以单独编写外键脚本并将其附加在脚本末尾:
foreach (ForeignKeyCollection fkcol in fkcolList) // Generate Relations
{
foreach (ForeignKey fk in fkcol)
{
StringCollection sc = fk.Script();
foreach (string s in sc)
{
sb.AppendLine(s);
}
}
}
fkcolList.Clear();
这可能不是最好的解决方案,因为连续三个循环,但对我来说它工作得很好。
您可能不需要手动遍历任何内容。只需将脚本程序配置为包括您想要的类型(表、存储过程、所有者,甚至数据)和 运行 它将处理依赖项(至少它为我生成了一个 DataOnly 脚本):
var server = new Server(@".\SQLExpress");
var database = server.Databases["mydb"];
var scripter = new Scripter(server);
//scripter.Options.WithDependencies = true; //didn't even need this option
scripter.Options.ScriptData = true;
scripter.Options.ScriptSchema = false;
var tables = database.Tables.Cast<Table>().Where(t => !t.IsSystemObject).ToList();
var scripts = scripter.EnumScriptWithList(tables.Select(t => t.Urn).ToArray());
return string.Join("\n", scripts.Select(s => s));