Скорость сравнения и синхронизации SQL

57
5

У меня есть два datatables, которые можно просто назвать их db1 и db2. db2 содержит все записи db1, но db1 не содержит всех записей db2 (оба они имеют одинаковые столбцы). Я должен каждый день проверять изменения в db1 и применять их для db2.

    В настоящее время мой инструмент "экспортирует" обе таблицы в DataTables, выполняет преобразование и обновляет/импортирует записи в db2:

SELECT * FROM db1db1_table

SELECT * FROM db2db2_table

for (int i = 0; i < db1_table.Rows.Count; i++)
{
for (int j = 0; j < db2_table.Rows.Count; j++)
{
//if db1_table.Rows[i] != db2_table.Rows[j] -> UPDATE db2 SET etc.
//if db1_table.Rows[i] doesn't exist in db2 -> INSERT INTO db2 etc.
}
}

Через некоторое время эта версия становится довольно медленной. Я говорю о десятках тысяч записей.

    Другая была моей первоначальной идеей, но я нашел ее медленной. Я вытаскиваю весь db1, просматриваю все его записи и каждый раз выполняю sql-запрос:

SELECT * FROM db1db1_table

for (int i = 0; i < db1_table.Rows.Count; i++)
{
//SELECT * FROM db2 WHERE "attributes LIKE db1_table.Rows[i]
//do the comparsion here and execute the UPDATE/INSERT commands if necessary
}

Какой из них быстрее (лучше)? Есть ли другой вариант, который у меня может быть?

спросил(а) 2014-01-14T18:46:00+04:00 6 лет, 8 месяцев назад
1
Решение
58

Замечание: вы действительно не должны хранить дубликаты данных в двух таблицах с той же структурой, в первую очередь...

Замечание: вы должны делать это обновление в SQL.

Чтобы ответить на ваш реальный вопрос. То, что вы испытываете, является алгоритмической сложностью O (N ^ 2). Его можно свести к O (N), если вы построите хэш-таблицу (словарь) одной из таблиц, и вы только переходите на другую. Когда вы ищете совпадение, вы смотрите в хеш-таблице вместо итерации, что вокруг O (1) вместо O (N). Вам просто нужно хорошее значение ключа, которое вы используете для хэширования.

Что-то вроде этого:

var dict = db2_table.Rows.Cast<DataRow>().ToDictionary(row2 => row2["keycolumn"].Value); // this is the hashing, make sure no duplicate keys exist!
foreach (DataRow row1 in db1_table.Rows) {
DataRow row2;
if (dict.TryGetValue(row1["keycolumn"].Value, out row2)) {
// row1 and row2 match by the key column, do something with them
dict.Remove(row2["keycolumn"].Value);
}
// else no match, row1 must be a new row
}
// now dict contains the keys from db2 which have no match in db1, they must have been deleted

ответил(а) 2014-01-14T18:54:00+04:00 6 лет, 8 месяцев назад
41

Существует еще одна опция O (n), если у вас есть уникальный идентификатор, который вы можете заказать и сравнить: закажите обе таблицы по идентификатору и пройдите их оба сразу, создав списки ожидающих изменений. После этого вы можете применить ожидающие изменения. Причина создания списков изменений заключается в том, что вы можете командовать командами вместе в конце обнаружения изменений и извлекать выгоду из таких вещей, как объемные вставки, CTE или временные таблицы для присоединения к удалению, а также группы групп для обновлений - все которые уменьшают один из самых больших источников латентности в этом типе работы: совлокальные поездки DB.

Основной цикл выглядит следующим образом:


// Assuming that IDs are long.  Change as required.
long db1_id;
long db2_id;
var idsToAppend = new List<long>();
var idsToUpdate = new List<long>();
var idsToDelete = new List<long>();
int i = 0;
int j = 0;
while (i < db1_table.Rows.Count && j < db2_table.Rows.Count) {
db1_id = db1_table.Rows[i]["ID"];
db2_id = db2_table.Rows[j]["ID"];
if (i == db1_table.Rows.Count && j < db2_table.Rows.Count) {
// There extra rows in the destination that have been removed from the source
idsToDelete.Add(db1_id);
j++;
} else if (j < db1_table.Rows.Count && j == db2_table.Rows.Count) {
// There extra rows in the source that need added to the destination
idsToAppend.Add(db1_id);
i++;
} else if (db1_id == db2_id) {
// On the same ID in both datasets
if !(db1_table.Rows[i] == db2_table.Rows[j]) {
// I know == won't work -- only do this if db1 may change and the changes must be propagated to db2
idsToUpdate.Add(db1_id);
}
i++;
j++;
} else if (db1_id > db2_id) {
// row in db1 was removed, remove row in db2
idsToDelete.Add(db1_id);
j++;
} else {
// implicit: db1_id < db2_id
// implicit: row in db1 doesn't exist in db2, needs added
idsToAppend(db1_id);
i++;
}
}
// Walk idsToAppend, idsToUpdate, and idsToDelete applying changes

ответил(а) 2014-01-14T20:15:00+04:00 6 лет, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема