问题摘要:
我们在多个服务器上都有一组数据库,这些数据库都应该具有相同的SQL对象。多年来,我们的开发人员已在各种数据库中添加/修改了对象,以使它们不再匹配。我需要从完全相同的多个服务器上的多个数据库中获取所有SQL对象(表,视图,存储过程,用户定义的函数)的列表。 (稍后获取唯一项列表,然后获取修改项列表)。我当前的解决方案有效,但是速度很慢。我想知道是否有更好的现有替代方法,但找不到。
当前解决方案:
目前,我一直在C#中使用SMO来获取所有对象的骨灰盒并将其编写脚本。当我尝试一次为它们编写1个对象的脚本时,该过程很慢(对服务器的大量调用)。如果我尝试通过将它们的骨灰盒打包到一个数组中来编写脚本,则过程会更快,但是我只是得到了生成的脚本的Enumerable或StringCollection,而没有组织该脚本来自哪个对象,等等。可以做到这一点(我知道现有的工具,例如ApexSQL或Red-Gate,目前尚无法解决)。我目前的解决方案是按名称将它们分组(并按服务器划分),并用较小的按批命名脚本编写脚本。
不好意思,我到处都是尝试不同方法的地方。也许有一种解决方案甚至不需要分析代码。需要注意的两件事:
//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文件,这些文件在多台服务器的所有数据库中完全相同。真是太慢了。我是否采用正确的方法?是否有更好/更现代的解决方案?
答案 0 :(得分:1)
所有数据库中都有包含对象的表。在sqlserver中,它是sysobj。首先,您需要创建一个主列表。您可以从所有数据库合并此视图,并执行不同的操作。然后将其外部连接到每个数据库sysobj