Как найти одинаковые, измененные и уникальные объекты SQL с нескольких серверов/баз данных?

Резюме проблемы:

У нас есть набор баз данных на нескольких серверах, которые «должны» иметь одни и те же объекты SQL. На протяжении многих лет наши разработчики добавляли/изменяли объекты в различных базах данных, чтобы они больше не совпадали. Мне нужно получить список всех объектов SQL (таблиц, представлений, хранимых процедур, определяемых пользователем функций) из нескольких баз данных на нескольких серверах, которые точно ОДИНАКОВЫ. (позже, чтобы получить список уникальных элементов, а затем список измененных элементов). Мое текущее решение работает, но довольно медленно. Я хотел знать, есть ли лучшая существующая альтернатива, но я не могу ее найти.

Текущее решение:

На данный момент я использую SMO в C#, чтобы получить урны всех объектов и написать их скрипт. Когда я пытаюсь написать им по одному объекту за раз, процесс идет медленно (много обращений к серверу). Если я попытаюсь заскриптовать их, упаковав их урны в массив, процесс пойдет быстрее, но я просто получу Enumerable или StringCollection результирующих скриптов без организации относительно того, из какого объекта был получен скрипт и т. д. Что было бы лучшим способом для подойти к этому (я знаю о существующих инструментах, таких как ApexSQL или Red-Gate, на данный момент о них не может быть и речи). Мое текущее решение состоит в том, чтобы сгруппировать их по именам (и разделить по серверам) и запрограммировать их в этих меньших пакетах по имени.

Извините за мой текущий код, я повсюду пробовал разные методы. Возможно, есть решение, для которого даже не нужно анализировать код. Две вещи, которые следует отметить:

  1. У меня есть класс SqlObjectInfo, в котором хранится только базовая информация о каждом объекте, например: имя, сервер, БД, схема, тип, урна.
  2. 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-файл (имя | тип) всех объектов, которые абсолютно одинаковы во всех БД на нескольких серверах. Это чертовски медленно. Правильно ли я подхожу к этому? Есть ли лучшее/более современное решение?


person Ryan    schedule 09.04.2019    source источник


Ответы (1)


Во всех базах данных есть таблицы, в которых есть объекты. В sqlserver это sysobj. Сначала вам нужно создать основной список. Вы можете объединить это представление из .all dbs и сделать отдельное. Затем внешнее соединение с каждым sysobj базы данных

person Saad Ahmad    schedule 10.04.2019