2010-10-12 6 views
2

У меня есть хранимая процедура, которая использует курсор FAST_FORWARD для хронологического цикла над набором записей ~ 300 000 и присваивает их наборам объявлений на основе состояния множества запущенных переменных и флаги, частично реализованные в виде переменных таблицы. Я много думал о том, как сделать это на основе набора, и я просто не могу этого сделать. Поэтому я придерживаюсь подхода курсора и мне нужно оптимизировать этот код.Сохраненная процедура с курсорным контуром FAST_FORWARD начинается быстро, заканчивается медленно

Я заметил, что первые 10% прогресса загружаются и обрабатываются очень быстро (2000 строк/сек), около 20% прогресса замедляется примерно до 300 строк в секунду, и в конце он замедляется до 60 строк/сек.

ИМО это может быть из-за 4 причины:

  • Курсор замедлится, который я думаю, вряд ли с FAST_FORWARD курсором
  • Обработка замедляющий. Для моих «групп» я использую переменные таблицы, в которые я вставляю, обновляю и удаляю. В любой момент времени макс. около 10 строк в этих переменных.
  • Вставка в таблицы целей замедляется. Я не понимаю, почему это было бы, у меня нет триггеров, определенных на них, и они просто обычные таблицы.
  • Зла магия

Либо это, либо мой процент счетчик сломан:

SET @curprogress = @curprogress + 1 
IF (@curprogress - ((@totprogress/100) * (FLOOR(@curprogress * 100/@totprogress)))) BETWEEN 0 AND 1 BEGIN 
    SET @msg = CAST(FLOOR(@curprogress * 100/@totprogress) AS VARCHAR) 
    RAISERROR('%s%s', 0, 1, @msg, '%...') WITH NOWAIT; 
END 

Кто-нибудь какой-нибудь ключ к разгадке, что искать и как идти по ускорению этого запроса?

Символическое отрывок из моего кода:

WHILE.... 
-- Fetch new record to be assigned to one of the open declaration sets 
FETCH NEXT INTO @row_field1, @row_field2.... 
IF (@flag2 = 1) AND ((@flag1 = 0) OR (@row_field1 <> @prevrow_field1)) 
BEGIN 
    -- Logging info: we are closing a child declaration set 
    INSERT INTO @logtable SELECT '--> LOG MESSAGE' 
    INSERT INTO @logtable 
    SELECT format_message(@row_field1, @calc_field2, field3...) 
    FROM @runningtable_sub S LEFT JOIN @runningtable_main M ON S.MainID = M.ID 

    -- Update enddate of parent 
    UPDATE M SET M.enddate = DATEADD(day,365,S.enddate) 
    FROM @runningtable_sub S 
    LEFT JOIN @runningtable_main M 
    ON S.MainID = M.ID 

    -- close and save child 
    INSERT INTO outputtable_main 
    SELECT @field1, COALESCE(Z.Field1,'NULL'), S.startdate, S.enddate, 
     M.Startdate, M.Enddate 
    FROM @runningtable_sub S 
    LEFT JOIN @runningtable_main M ON S.MainID = M.ID 

    -- delete child from running table 
    DELETE FROM @runningtable_sub WHERE S.enddate < @curdate 
END 

ответ

0

я мог придумать множество причин, это замедление, но это трудно сузить ее, не зная мощность ваших данных.

Случайные наблюдения:

    функция

  1. Ваш format_message(), безусловно, собака; все UDF. Но сколько строк вы вставляете за проход?
  2. @runningtable_main никогда не очищается.
  3. Цены на обновления
  4. удаление дорогое. При использовании временных таблиц и переделок реализации, вы можете укоротить вместо удаления

Чтобы понять некоторые из них для себя, добавить приборов:

DECLARE @now DATETIME, @duration INT, @rowcount INT 

WHILE.... 
-- Fetch new record to be assigned to one of the open declaration sets 
FETCH NEXT INTO @row_field1, @row_field2.... 
IF (@flag2 = 1) AND ((@flag1 = 0) OR (@row_field1 <> @prevrow_field1)) 
BEGIN 
    PRINT '---------------' 

    -- Logging info: we are closing a child declaration set 
    INSERT INTO @logtable SELECT '--> LOG MESSAGE' 
    SET @now = GETDATE() 
    INSERT INTO @logtable 
    SELECT format_message(@row_field1, @calc_field2, field3...) 
    FROM @runningtable_sub S LEFT JOIN @runningtable_main M ON S.MainID = M.ID 
    SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE) 
    RAISERROR('%i row(s) inserted into @logtable, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT 

    -- Update enddate of parent 
    SET @now = GETDATE() 
    UPDATE M SET M.enddate = DATEADD(day,365,S.enddate) 
    FROM @runningtable_sub S 
    LEFT JOIN @runningtable_main M 
    ON S.MainID = M.ID 
    SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE) 
    RAISERROR('%i row(s) updated in @runningtable_main, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT 

    -- close and save child 
    SET @now = GETDATE() 
    INSERT INTO outputtable_main 
    SELECT @field1, COALESCE(Z.Field1,'NULL'), S.startdate, S.enddate, 
     M.Startdate, M.Enddate 
    FROM @runningtable_sub S 
    LEFT JOIN @runningtable_main M ON S.MainID = M.ID 
    SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE) 
    RAISERROR('%i row(s) inserted into outputtable_main, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT 

    -- delete child from running table 
    SET @now = GETDATE() 
    DELETE FROM @runningtable_sub WHERE S.enddate < @curdate 
    SELECT @rowcount = @@ROWCOUNT, @duration = DATEDIFF(ms,@now,GETDATE) 
    RAISERROR('%i row(s) deleted from @runningtable_sub, %i milliseconds',-1,-1,@rowcount,@duration) WITH NOWAIT 
END 

Если вы включили весь свой код (в том числе объявление курсора), а не просто символический отрывок, кто-то здесь, вероятно, мог бы переработать его для того, чтобы вы были намного эффективнее и/или избегали курсоров все вместе.

+0

Я не могу опубликовать весь код, постараюсь больше разместить сообщение. Основная таблица очищается в другом месте. Я вставляю между 0-3 строк за проход. Являются ли обновления и удаления слишком дорогостоящими для переменных таблицы? Курсор находится в режиме просмотра, который обычно загружается через 10-16 секунд. Я изменил курсор на переменную таблицы и цикл WHILE, но никаких изменений. Поэтому проблема должна быть где-то в теле. – thomaspaulb

+0

Это оказалось «ОБНОВЛЕНИЕ ОТ ..LEFT JOIN', используя ваше временное оборудование. Он также идет медленнее с каждой строкой. Странно, что проблема теперь исчезла сама по себе, хотя я не сделал никаких заметных изменений в СП. Я в недоумении, но я переписал процедуру с помощью сгруппированного ввода, поэтому мне больше не нужно использовать рабочие таблицы, но работать с работающими переменными. Это намного быстрее само по себе, и никаких утверждений 'UPDATE' больше нет. Я приму свой ответ за битку приборов. – thomaspaulb

Смежные вопросы