Можно ли оптимизировать этот запрос? - PullRequest
1 голос
/ 14 ноября 2010

Я попытался выполнить следующий запрос:

update ms
    set user_B_total_duration = amc.total_duration
    from monthly_statistics ms
        inner join aggregate_monthly_conversations amc
            on ms.user_B = amc.user_B

но запрос выполняется более 10 часов. В каждой таблице около 23 миллионов записей (month_statistics и aggregate_monthly_conversations). Ядром базы данных является SQL Server 2008, а ПК - четырехъядерный процессор 2,66 ГГц, 4 ГБ ОЗУ.

Кто-нибудь знает, возможно ли оптимизировать приведенный выше запрос или выполнить ту же задачу с помощью любого обходного пути?

Ответы [ 5 ]

1 голос
/ 14 ноября 2010

Если бы я решал эту проблему, я бы хотел найти следующие вещи:

  1. Если это практически возможно, убедитесь, что никто не монополизирует таблицу (т. Е. Блокирует ее)
  2. Убедитесь, что соединяемые столбцы проиндексированы (т. Е. ms.user_B, amc.user_B)
  3. Обновите столбцы в пакетах, используя UPDATE TOP (100) ms SET ...

Элемент 3 на самом деле довольно хорошважно при выполнении больших вставок / обновлений / удалений.SQL Server создает журнал, чтобы отменить эту операцию, если она частично завершается сбоем, и это становится все более дорогостоящим.Если вам нужно обновить ряды шириной 1 м, это может быть намного быстрее для работы с 20 пакетами по 50 000 строк.Я видел совет, утверждающий, что это имеет огромное значение (и это делает AFAICT).Кроме того, это препятствует тому, чтобы запросы для таблицы увеличивались в очереди.

Но есть два предостережения: 1. Вы будете фиксировать каждый пакет отдельно, поэтому вы хотите убедиться, что ваша операция может выдержать «частично выполненный»,(Я предполагаю, что этот можно просто перезапустить.) 2. Вы должны быть в состоянии определить, какие столбцы обновляются.

Итак, в вашем случае, возможно:

declare @update_date datetime;
set @update_date = getdate();

while 1 = 1
begin
    update top(10000) ms set
        user_B_total_duration = amc.total_duration,
        last_updated = @update_date
    from
        monthly_statistics ms
        inner join aggregate_monthly_conversations amc
        on ms.user_B = amc.user_B
    where
        ms.last_updated < @update_date;

    if @@rowcount = 0 break;
end

Вы также можете добавить отпечаток, чтобы сказать, как далеко вы находитесь.

0 голосов
/ 14 ноября 2010

Более простой вопрос: почему вы даже занимаетесь первыми в этой дорогой денормализующей пакетной обработке на конец месяца, когда вы можете получить total_duration любого пользователя с помощью специального запроса?Каково специфическое обоснование для использования этого подхода, не относящегося к РСУБД?Обычно люди прибегают к пакетной обработке данных на конец месяца, когда специальный запрос слишком дорогой и медленный для специальных отчетов.Так ли это в вашем случае?

С индексами для соединенных столбцов, ms.user_b и amc.user_b, вы сможете получить total_duration любого пользователя с помощью простого объединения двух таблиц.Сколько разных пользователей можно найти среди 23M записей?Если столбец ms.user_b имеет низкое количество элементов, возможно, составной индекс, такой как (ms.user_b, timeperiod) или что-то сравнимое (мы не знаем вашу схему), приведет к требуемой производительности ad hoc без неприемлемого снижения производительности при вставках / обновлениях?

Если вам нужно оставить все как есть, вы можете попробовать хранимую процедуру, в которой вы выбираете отдельный набор AMC.user_ids в курсор, и обрабатывать обновление таблицы MS по одному идентификатору за раз.:

 ...
 from monthly_statistics ms 
 inner join aggregate_monthly_conversations amc 
 on ms.user_B = amc.user_B  and ms.user_b = @currentuserid

Для этого также потребуется хотя бы один простой индекс: для столбца ms.user_b или составной индекс для (ms.user_b, {некоторых других столбцов)}).

0 голосов
/ 14 ноября 2010

Индексы monthly_statistics.User_B и aggregate_monthly_conversations.User_B будут отличным началом, возможно, , включая total_duration в индексе aggregate_monthly_conversations.User_B.

0 голосов
/ 14 ноября 2010

Похоже, что вы полностью исчерпали память на этом компьютере, а SQL Server выгружает память на диск.4 Гб оперативной памяти не так много.

Сколько времени занимает выполнение этого запроса?Сколько строк возвращено?

select 
'update monthly_statistics set user_B = ' +
CAST(amc.total_duration as varchar) + ' ' +
'where user_B = ' + 
CAST(ms.user_B as varchar) + ' GO' 
from monthly_statistics ms 
inner join aggregate_monthly_conversations amc 
on ms.user_B = amc.user_B 

Вы можете использовать эти выходные данные для обновления таблицы.

0 голосов
/ 14 ноября 2010

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

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