Обновление таблицы с миллионами записей, прошло 4 дня

12

В настоящее время я обновляю таблицу с миллионами записей, прошло 4 дня, и запрос все еще выполняется.

Я проверил монитор активности, показывает, что запрос запущен.

В журнале событий нет ошибок вообще.

Производительность мудрая:

  • Tempdb на диске A (850 ГБ свободного места)
  • файл базы данных на диске B (750 ГБ свободного места)
  • 16 ГБ оперативной памяти

Пожалуйста, предложите мне, что мне делать?

Запрос

UPDATE
    dbo.table1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
    dbo.table2 t2
WHERE
    LEFT(dbo.test1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 
Счастливый
источник

Ответы:

3

В этом запросе есть интересная деталь, которую я сначала не заметил. Благодаря ответу Фабрицио Араужо теперь я вижу его: у вас есть доступ к двум таблицам. Я никогда не видел такого использования оператора обновления раньше, и я не советую использовать его. Я рекомендую вам использовать более интуитивно понятный синтаксис соединения для ответа Fabricio.

Вероятная причина заключается в том, что объединение двух таблиц создает огромное количество строк. Это может произойти, если LEFT(col, 3)выражение выдает повторяющиеся значения. Если будет получено 10 дубликатов, это приведет к 100000x100000 = 10000000000 строк в результате объединения.

Я не думаю, что индексация играет здесь важную роль. SQL Server может разрешить это неиндексированное соединение с помощью хэша или объединения слиянием. Не занимает 4 дня.

Другая вероятная причина - недооценка мощности входных или выходных данных объединения. SQL Server мог выбрать соединение цикла.

Поскольку это все еще предположение, я рекомендую опубликовать план запроса, который пролил бы свет на эту проблему.

USR
источник
8

Этот запрос требует от вас сканировать каждую строку в таблице, потому что

  • Я думаю, что procodet или ProviderCode не проиндексированы
  • Даже если они были проиндексированы, у вас есть LEFT, который является функцией предиката WHERE
  • И у вас тоже есть COLLATE, который фактически является функцией предиката WHERE

«функция предиката WHERE» означает, что индексы не будут использоваться

Если вы пакетируете его (скажем, UPDATE TOP (10000) ... AND costPercentage IS NULL), то вам нужен индекс для costPercentage, и это предполагает, что вы его устанавливаете.

Единственные решения, которые я вижу,

  • заполнить новую таблицу партиями, основываясь, скажем, на первичном ключе
  • создать индексированные вычисляемые столбцы, чтобы скрыть выражения LEFT и COLLATE, а затем запустить обновление
ГБН
источник
@ gbn .. спасибо, это отличная идея .. но поскольку данные исчисляются миллионами, этот процесс займет время .... я подумал, может быть, есть способ узнать ход выполнения запроса?
Лаки
1
Почему для сканирования «миллионов» строк требуется 4 дня? Независимо от того, насколько большими и сильно проиндексированными могут быть строки, это не должно занять 4 дня. Корень проблемы до сих пор неизвестен.
USR
1
Если вы регулярно имеете дело с большими данными, как насчет того, чтобы получить подходящий сервер для этого? Поместите данные на SSD и т. Д.
TomTom
1
@ Да, конечно. Я обращался к ответу. Что-то не так, что мы еще не нашли. Это не сам запрос или оборудование. Это никогда не составит 4 дня.
USR
3
Учитывая, что запрос соединяет 3-символьную часть столбца с 3-символьной частью другого столбца, результат, скорее всего, будет содержать дубликаты. Это намного хуже, чем просто обновление миллионов строк. Могу поспорить, что он сканирует рабочий стол на миллиарды.
Датагод
4

Прежде всего, измените запрос на:

UPDATE t1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
  dbo.table1 t1
  inner join dbo.table2 t2
    on LEFT(t1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 

Как указано в первом сообщении Джеффа Модена в этом обсуждении , ваш запрос очень похож на тот, который он предупреждал о «эффекте Хэллоуина».

После этого эти ЛЕВЫЕ выражения должны быть проиндексированы. Ответ ГБН даст вам указания, как это сделать.

Фабрицио Араужо
источник