#.net #vb.net #dictionary
#.net #vb.net #словарь
Вопрос:
У меня есть процесс, который ежедневно импортирует файл с регистрациями продуктов и добавляет их в нашу базу данных. Первоначально этот процесс запрашивал бы базу данных несколько раз для каждой записи, чтобы определить, как обрабатывать данные.
Чтобы ускорить этот процесс и предотвратить любые проблемы с таймаутом, которые могут возникнуть у людей, пытающихся использовать сайт отчетов, использующий ту же базу данных, я изменил код, чтобы преобразовать несколько таблиц в словари, а затем выполнить итерацию по ним, чтобы увидеть, существует ли этот клиент / адрес / дилерский центр, и извлечь идентификатор из ключа словаря, если он существует, или вставить его в таблицу, а затем в словарь, если нет.
Однако в настоящее время я обнаружил, что это выполняется медленнее, чем если бы я запрашивал базу данных несколько раз для каждой регистрации. Одна из возможных причин, о которой я могу думать, заключается в том, что мои словари довольно большие (в одном 8 миллионов записей, а в другом 11 миллионов).
Вот один из примеров того, что я делаю:
For Each kvp As KeyValuePair(Of Int64, String) In dCust
If kvp.Value = firstName amp; "|" amp; lastName amp; "|" amp; companyName amp; "|" amp; addrId amp; "|" amp; typeID amp; "|" amp; phone amp; "|" amp; email Then
custId = kvp.Key
Exit For
End If
Next
В этом словаре содержится около 11 миллионов записей.
Идея, которая пришла в голову моему коллеге, заключалась в том, чтобы запустить Dictionary.ContainsValue()
перед циклом, чтобы посмотреть, есть ли он вообще. И если это не так, пропустите цикл полностью. Я бы хотел попробовать это, только если это выполняется быстрее, чем просто выполнение самого цикла, если они занимают одинаковое время, я не вижу смысла в повторном запуске цикла. дважды.
Итак, мои вопросы к вам:
- Делаю ли я это наиболее эффективным способом?
- Было бы быстрее запустить
Dictionary.ContainsValue()
перед попыткой выполнения цикла, или система интерпретирует их как одно и то же, тем самым удваивая мое время? - Есть ли что-нибудь еще, что я должен искать?
Комментарии:
1. Вы выполняете вычисления
firstName amp; "|" amp; lastName amp; "|" amp; companyName amp; "|" amp; addrId amp; "|" amp; typeID amp; "|" amp; phone amp; "|" amp; email
11 миллионов раз? Попробуйте вычислить это только один раз перед циклом.2. @Gabe, ты опередил меня на 50 секунд!
3. Всегда ли Dictionary.Values уникален? Если это так, инвертируйте ваши пары ключ-значение (как отмечается в нескольких ответах) — хорошая идея.
Ответ №1:
Одной очевидной небольшой оптимизацией было бы выполнение конкатенации firstName
, lastName
и т.д. один раз вне цикла. В настоящее время вы выполняете объединение на каждой итерации цикла, что, очевидно, происходит медленнее, чем могло бы быть.
Нет, использование ContainsValue
не было бы быстрее — все равно придется выполнять линейный поиск.
Очевидной большой оптимизацией было бы инвертировать словарь — создать Dictionary(Of String, Int64)
который в основном имеет идентификатор для каждого строкового значения. В настоящее время вы не используете естественные преимущества словаря — вы, по сути, рассматриваете его как список пар ключ / значение.
Вы действительно используете словарь обычным способом (просматриваете по ключу)?
Ответ №2:
Похоже, вы используете словарь противоположным образом тому, как его следует использовать, или я что-то упускаю?
Повторяя пары ключ-значение в словаре, вы сводите на нет преимущество, которое предоставляет словарь (хэш-таблица) — быстрый поиск заданного значения ключа.
Вы должны использовать словарь (String, Int64), сопоставляя firatname, lastname, … с CustID. Поиск в этом будет очень быстрым по сравнению с тем, что вы делаете в данный момент.
Ответ №3:
Одна вещь, которую вы могли бы сделать, чтобы ускорить процесс, — это предварительно объединить эту строку поиска:
Dim SearchValue as String = firstName amp; "|" amp; lastName amp; "|" amp; companyName amp; "|" amp; addrId amp; "|" amp; typeID amp; "|" amp; phone amp; "|" amp; email
For Each kvp As KeyValuePair(Of Int64, String) In dCust
If kvp.Value = SearchValue Then
custId = kvp.Key
Exit For
End If
Next
Ответ №4:
Смысл использования словаря в том, чтобы выполнить быстрый поиск по КЛЮЧУ, а не по значению. Либо просто используйте обычный список массивов, либо измените свой код так, чтобы вы выполняли поиск по ключу, а не по значению.
Ответ №5:
Я думаю, что ответы о словаре великолепны, но я думаю, что более широкий ответ заключается в том, чтобы обрабатывать этот материал на уровне базы данных, а не загружать миллионы записей для повторения с использованием словаря в C #. Почему бы не использовать табличный параметр (я предполагаю, что вы используете SQL Server 2008) для передачи данных, которые вы хотите сравнить, и посмотреть, существуют ли они? Вы бы передали его в сохраненную процедуру или что-то еще, что выполняло бы сравнение всех на стороне SQL. Вы могли бы даже сделать что-то вроде:
INSERT ProductRegistrations
SELECT * FROM @tvpProductsToAdd pa WHERE
pa.firstName pa.lastName pa.companyName NOT IN
(SELECT firstName lastName companyName FROM ProductRegistrations)
@tvpProductsToAdd — это параметр с табличным значением, который вы передаете в своих новых продуктах. Возможно, вам захочется создать какой-то индекс для этих полей, чтобы ускорить сравнение, учитывая, что у вас, похоже, нет ключей, которые вы могли бы сравнить.