Как найти одинаковые, модифицированные и уникальные объекты SQL на нескольких серверах / базах данных? - PullRequest
1 голос
/ 10 апреля 2019

Описание проблемы:

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

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

На данный момент я использую SMO в C #, чтобы получить урны всех объектов и написать их. Когда я пытаюсь написать им по 1 объекту за раз, процесс идет медленно (много обращений к серверу). Если я попытаюсь создать сценарий, упаковав их урны в массив, процесс будет быстрее, но я просто получу 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-файл (Name | Type) для всех объектов, которые одинаковы во всех БД на нескольких серверах. Это так чертовски медленно. Правильно ли я подхожу к этому? Есть ли лучшее / более современное решение?

1 Ответ

1 голос
/ 10 апреля 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...