问题总结:
我们在多台服务器上有一组数据库,它们“应该”都具有相同的 SQL 对象。多年来,我们的开发人员在各种数据库中添加/修改了对象,使它们不再匹配。我需要从完全相同的多个服务器上的多个数据库中获取所有 SQL 对象(表、视图、存储过程、用户定义函数)的列表。(稍后获取唯一项目列表,稍后获取修改项目列表)。我目前的解决方案有效,但速度很慢。我想知道是否有更好的现有替代方案,但我找不到。
当前解决方案:
现在我一直在 C# 中使用 SMO 来获取所有对象的 urns 并编写脚本。当我尝试一次为它们编写 1 个对象时,该过程很慢(大量调用服务器)。如果我尝试通过将它们的骨灰盒打包到一个数组中来编写脚本,这个过程会更快,但我只是得到一个 Enumerable 或 StringCollection 生成的脚本,而没有组织脚本来自哪个对象等。什么是更好的方法接近这个(我知道现有的工具,如 ApexSQL 或 Red-Gate,它们暂时不存在)。我目前的解决方案是按名称对它们进行分组(并按服务器拆分),并在那些较小的按名称批次中编写脚本。
请原谅我当前的代码,我一直在尝试不同的方法。也许有一个甚至不需要分析代码的解决方案。有两点需要注意:
- 我有一个名为 SqlObjectInfo 的类,它只存储每个对象的一些基本信息,例如:名称、服务器、数据库、架构、类型、Urn
- items 是一个 SqlObjectInfoCollection ,它是一个包含 SqlObjectInfo 列表以及一些帮助功能从服务器和数据库添加对象的类。用所有 SqlObjectInfo 填充这个集合很快,所以这不是问题。
//Create DataTable
var table = new DataTable("Equal Objects");
table.Columns.Add("Name");
table.Columns.Add("Type");
//Create DataRows
int dbCount = items.SqlObjects.GroupBy(obj => obj.Database).Count();
DMP dmp = DiffMatchPatchModule.Default;
var rows = new List<DataRow>();
foreach (IGrouping<string, SqlObjectInfo> nameGroup in items.SqlObjects.GroupBy(obj => obj.Name))
{
var likeNamedObjs = nameGroup.ToList();
if (likeNamedObjs.Count != dbCount)
{
continue; //object not in all databases
}
//Script Objects
var rawScripts = new List<string>();
bool scriptingSucceeded = true;
foreach (IGrouping<Server, SqlObjectInfo> serverGroup in nameGroup.GroupBy(obj => obj.Server))
{
Server server = serverGroup.Key;
Urn[] urns = serverGroup.Select(obj => obj.Urn).ToArray();
var scripter = new Scripter(server)
{
Options = items.ScriptingOptions
};
IEnumerable<string> results;
try
{
results = scripter.EnumScript(urns);
}
catch (FailedOperationException)
{
scriptingSucceeded = false;
break; //the object is probably encrypted
}
rawScripts.AddRange(results);
}
if (!scriptingSucceeded)
{
continue;
}
if (rawScripts.Count % nameGroup.Count() != 0)
{
continue;
}
var allScripts = new List<string>();
int stringsPerScript = rawScripts.Count / nameGroup.Count();
for (int i = 0; i < rawScripts.Count; i += stringsPerScript) //0, 3, 6, 9
{
IEnumerable<string> scriptParts = rawScripts.Skip(i).Take(stringsPerScript);
allScripts.Add(string.Join(Environment.NewLine, scriptParts));
}
//Compare Scripts
bool allEqual = true;
for (int i = 1; i < allScripts.Count; i++)
{
(string lineScript0, string lineScriptCurr, _) = dmp.DiffLinesToChars(allScripts[0], allScripts[i]).ToValueTuple();
List<Diff> diffs = dmp.DiffMain(lineScript0, lineScriptCurr, false);
if (!diffs.TrueForAll(diff => diff.Operation.IsEqual))
{
allEqual = false;
break; //scripts not equal
}
}
//If all scripts are equal, create data row for object
if (allEqual)
{
DataRow row = table.NewRow();
row["Name"] = likeNamedObjs[0].Name;
row["Type"] = likeNamedObjs[0].Type;
rows.Add(row);
}
}
//Add DataRows to DataTable
foreach (DataRow row in rows.OrderBy(r => r["Type"]).ThenBy(r => r["Name"]))
{
table.Rows.Add(row);
}
//Write DataTable to csv
var builder = new StringBuilder();
builder.AppendLine(string.Join(",", table.Columns.Cast<DataColumn>().Select(col => col.ColumnName)));
foreach (DataRow row in table.Rows)
{
builder.AppendLine(string.Join(",", row.ItemArray.Select(field => field.ToString())));
}
File.WriteAllText("equalObjects.csv", builder.ToString());
该代码有效。我可以得到我预期的(名称|类型)所有对象的 csv 文件,这些对象在跨多个服务器的所有数据库中完全相同。简直太慢了 我以正确的方式接近这个吗?有更好/更现代的解决方案吗?