Производительность курсора - PullRequest
1 голос
/ 28 февраля 2011

У меня довольно большой скрипт, который использует курсор и вложенные в него курсоры.

Я столкнулся с проблемой производительности, я обнаружил, что последняя инструкция в скрипте, которая завершает основной цикл while, занимает большую часть времени:

SET STATISTICS TIME ON

FETCH NEXT FROM OldMetaOffer_cursor
INTO @MetaOfferId, @CustomerId, @OfferName, @CheckedOutById, @CheckOutDate, @LastOfferStatusId, @LastCalculationNumber, @CreatedByDisplayName, @CreatedById, @CreateDate, @CoordinatorId, @CoordinatorDate, @CentralAnalystId, @CentralAnalystDate, @DeployUserId, @DeploymentDate, @OwnerId;

SET STATISTICS TIME OFF

Время выполнения SQL Server:

  • Время ЦП = 0 мс, истекшее время = 0 мс.
  • Время выполнения SQL-сервера:
  • Процессорное время = 4328 мс, прошедшее время = 4335 мс.

Это занимает более 4 секунд, в то время как один шаг в общей сложности занимает 4,6 с

Таблица мета-предложенийимеет ~ 150 тыс. строк, но я использую курсор на 8,5 тыс. строк.(Я фильтрую строки в начале).

Есть ли способ улучшить эту низкую производительность?

В начале цикла у меня есть:

DECLARE   @MetaOfferId uniqueidentifier     
    , @MetaOfferTypeId int
    , @CustomerId  uniqueidentifier         -- CustomerId
    , @OfferName nvarchar(50)               -- OfferName
    , @CheckedOutById int                   -- CheckOutById
    , @CheckOutDate datetime                -- CheckOutDate
    , @LastOfferStatusId int                -- LastProcessStatusId            
    , @LastCalculationNumber nvarchar(20)   -- LastCalculationNumber
    , @CreatedByDisplayName nvarchar(300)   -- CreatedByDisplayName
    , @CreatedById int                      -- CreatedById
    , @CreateDate datetime                  -- CreateDate
    , @CoordinatorId int                    -- CoordinatorId
    , @CoordinatorDate datetime             -- CoordinatorDate
    , @CentralAnalystId int                 -- CentralAnalystId
    , @CentralAnalystDate datetime          -- CentralAnalystDate
    , @DeployUserId int                     -- DeployUserId
    , @DeploymentDate datetime              -- DeploymentDate
    , @OwnerId int                          -- OwnerId
    -- id statusu po zmapowaniu
    , @NewLastOfferStatusId int             



DECLARE OldMetaOffer_cursor CURSOR FOR
SELECT  MetaOfferId, CustomerId, OfferName, CheckedOutById, CheckOutDate, LastOfferStatusId, LastCalculationNumber, CreatedByDisplayName, CreatedById, 
        CreateDate, CoordinatorId, CoordinatorDate, CentralAnalystId, CentralAnalystDate, DeployUserId, DeploymentDate, OwnerId

FROM [Other].[dbo].[MetaOffer] MO where 
    exists 
    (select * from [Other].[dbo].[OfferHistoryItem] 
    where MetaOfferId = MO.MetaOfferId  and NewStatusId = 9 and DiscountId is null and KoosOfferId is null) 

Может бытьесть проблема в том, что при следующей выборке этот запрос делается еще раз?Эти результаты нигде не буферизируются.Если да, то есть ли способ обналичить результат этого запроса и работать с данными, не выполняя запрос на каждом шаге цикла?

Ответы [ 3 ]

5 голосов
/ 28 февраля 2011

Поскольку вы пропустили самую важную часть проблемы (что на самом деле делают курсоры), я просто дам вам эту справку, которая, надеюсь, должна показать вам, как выполнять вашу задачу без курсоров. Курсоры крайне плохо работают и не должны использоваться, если существует какая-либо другая альтернатива. Однажды я изменил процесс с 45 минут до менее минуты, удалив курсор, а другой - с 24 часов до 30 минут. Причин использования курсора очень мало, а многих - не использовать. Это техника последней инстанции, а не первое, что вы пробуете.

http://wiki.lessthandot.com/index.php/Cursors_and_How_to_Avoid_Them

2 голосов
/ 28 февраля 2011

Если вы посмотрите на документацию для CURSOR для какой-либо разумно текущей версии SQL Server, и я подумаю, что также есть несколько других СУБД, вы обнаружите, что в ней указаны состояния НЕ ИСПОЛЬЗОВАТЬ CURSOR и объяснение, для которого они включены. только обратная совместимость.

Есть много способов их избежать, но это зависит от того, с каким сервером баз данных вы работаете.

Например, вы можете использовать временную таблицу:

SELECT 
  [ID], [col1], [col2] 
INTO
  #stuff
WHERE
  [col1] LIKE '%something%'

DECLARE @id int, @c1 varchar(32), @c2 varchar(32)

SELECT TOP 1 @id = [ID], @c1 = [col1], @c2 = [col2]
FROM #stuff

WHILE @@ROWCOUNT > 0
BEGIN
  -- do something, say execute a stored procedure, for each row
  EXEC someproc @id, @c1, @c2

  SELECT TOP 1 @id = [ID], @c1 = [col1], @c2 = [col2]
  FROM #stuff
  WHERE [ID] > @id
END

В зависимости от того, что делает код и какую базу данных вы используете, могут быть гораздо более эффективные решения, но временные таблицы в любом случае обеспечат значительное улучшение по сравнению с курсорами.

Если вы используете SQL Server, помните, что если вы пишете скалярные функции, которые обращаются к таблицам (на практике это выполняет операторы SELECT или вызывает что-то еще), такая функция фактически становится курсором - поэтому избегайте этого всякий раз, когда можете.

0 голосов
/ 28 февраля 2011

Курсоры просто медленные. Вложенные курсоры еще медленнее. Кроме этого нам нужно было бы увидеть гораздо более конкретную информацию, чтобы дать полезный совет.

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