Эффективный способ сравнения таблиц данных - PullRequest
6 голосов
/ 07 мая 2020

Ниже приведен метод c# для сравнения двух таблиц данных и возврата записей о несовпадении.

public DataTable GetTableDiff(DataTable dt1, DataTable dt2, string columnName)
{
    var StartTime = DateTime.Now;
    dt1.PrimaryKey = new DataColumn[] { dt1.Columns["N"] };
    dt2.PrimaryKey = new DataColumn[] { dt2.Columns["N"] };

    DataTable dtDifference = null;
    //Get the difference of two datatables
    var dr = from r in dt1.AsEnumerable()
             where !dt2.AsEnumerable().Any(r2 => r["N"].ToString().Trim().ToLower() == r2["N"].ToString().Trim().ToLower()
                 && r[columnName].ToString().Trim().ToLower() == r2[columnName].ToString().Trim().ToLower())
             select r;

    if (dr.Any())
    {
        dtDifference = dr.CopyToDataTable();
    }
    return dtDifference;
}

Этот код работает, но для сравнения 10 000 записей в таблице данных требуется 1,24 минуты. Есть ли способ сделать это быстрее?

N - это первичный ключ, а columnName - это столбец для сравнения.

Спасибо.

Ответы [ 2 ]

3 голосов
/ 07 мая 2020

Сначала я хотел бы спросить, пробовали ли вы это в простом for / foreach l oop вместо этого и сравнивали производительность?

В настоящий момент вы создаете новый Enumerable, а затем копируете его в datatable. Если вы используете for / foreach l oop, тогда вы можете сравнивать и копировать в той же итерации.

Вам также следует посмотреть на сравнение строк. В данный момент вы обрезаете, а затем конвертируете в нижний регистр. Это позволит выделить новую память для каждой операции для каждой строки, поскольку строки неизменяемы. Итак, в вашем операторе where вы в основном делаете это (до) 8 раз за итерацию.

Я бы также спросил, действительно ли вам нужно Trim()? Вероятно ли, что у одного DT будет пробел перед строкой, а у другого - нет? Или сравнение все равно будет правдой? Не обрезайте строки, если это действительно не нужно.

Тогда вам следует использовать сравнение строк без учета регистра, а не преобразование ToLower. Это будет быстрее. Согласно MS StringComparison.OrdinalIgnoreCase работает лучше.

Сделайте это, а затем сравните производительность и посмотрите, какая у вас разница

См. Также: https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings

Обновление:

Это заинтриговало меня, поэтому я вернулся и провел несколько тестов. Я сгенерировал 10000 строк случайных (i sh) данных в двух таблицах данных, где каждая вторая строка соответствовала бы и выполнила ваше сравнение по сравнению с упрощенным для l oop сравнением со сравнением строк следующим образом:

  for (int i = 0; i < dt1.Rows.Count; i++)
  {
      if (dt1.Rows[i]["N"].ToString().Equals(dt2.Rows[i]["N"].ToString(), StringComparison.OrdinalIgnoreCase)
          && dt1.Rows[i][columnName].ToString().Equals(dt2.Rows[i][columnName].ToString(), StringComparison.OrdinalIgnoreCase))
      {
          dtDifference.Rows.Add(dt1.Rows[i].ItemArray);
      }
  }

Ваш код = 66 000 мс -> 75 000 мс

Для l oop код = 12 мс -> 20 мс

Значительная разница!

Затем я провел сравнение, используя для метода l oop, но с двумя разными типами сравнения строк для строки. Используя мое сравнение строк с вашим. Но для этого мне пришлось протестировать 1 миллион строк, чтобы получить значительную разницу.

Эта разница составляет от 200 мс до 800 мс

Таким образом, в этом случае кажется, что сравнение строк не является основной фактор.

Таким образом, похоже, что ваш запрос Linq, создающий строки данных, занимает большую часть времени, а не сравнение самих строк.

Итак, переключитесь на использование for l oop, и снова все будет хорошо в мире!

1 голос
/ 07 мая 2020

Если значения string в DataTable были обрезаны, вы можете использовать метод DataRowCollection.Find для быстрого поиска на основе первичного ключа в сочетании с DataTable.CaseSensitive свойство. Но поскольку это не так, вы не можете использовать встроенные функции ADO. NET. К счастью, sh то же самое легко сделать, используя метод ToLookup LINQ, который возвращает словарь «один ко многим», доступный только для чтения.

public DataTable GetTableDiff(DataTable dt1, DataTable dt2, string columnName)
{
    IEqualityComparer<string> comparer = StringComparer.OrdinalIgnoreCase;

    var lookup = dt2.AsEnumerable().ToLookup(row => row["N"].ToString().Trim(),
        comparer);

    var diffList = dt1.AsEnumerable()
        .Where(r1 => !lookup[r1["N"].ToString().Trim()].Any(r2 => comparer.Equals(
            r1[columnName].ToString().Trim(), r2[columnName].ToString().Trim())))
        .ToList();

    if (diffList.Count == 0) return null;
    return diffList.CopyToDataTable();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...