Каковы различные способы замены курсора?

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

Какую проблему решал курсор? Как вы заменили курсор?

Ответы на вопрос(4)

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

Чаще всего подойдет SELECT с правильными параметрами - или, возможно, вы имеете дело с оператором UPDATE.

Суть в том, что вы должны начать думать об операциях над множествами и рассказать своей СУБД, что вы хотите сделать, а не как делать это шаг за шагом.

Трудно дать один, "правильный" ответ на этот вопрос ... вам почти придется показать это на конкретном примере.

Марк

Я заменил некоторые курсоры на циклы WHILE.

DECLARE @SomeTable TABLE
(
     ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
     SomeNumber int,
     SomeText varchar
)

DECLARE @theCount int
DECLARE @theMax int

DECLARE @theNumber int
DECLARE @theText varchar

INSERT INTO @SomeTable (SomeNumber, SomeText)
SELECT Number, Text
FROM PrimaryTable

SET @theCount = 1
SELECT @theMax = COUNT(ID) FROM @SomeTable

WHILE (@theCount <= @theMax)
BEGIN

     SET @theNumber = 0
     SET @theText = ''

     SELECT @theNumber = IsNull(Number, 0), @theText = IsNull(Text, 'nothing')
     FROM @SomeTable
     WHERE ID = @theCount

     -- Do something.
     PRINT 'This is ' + @theText + ' from record ' + CAST(@theNumber AS varchar) + '.'

     SET @theCount = @theCount + 1

END

PRINT 'Done'
Решение Вопроса

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

INSERT INTO YourTable
        (col1, col2, col3, col4)
    SELECT
        cola, colb+Colz, colc, @X
        FROM ....
            LEFT OUTER JOIN ...
        WHERE...

Когда вы смотрите на петлю, посмотрите, что она делает внутри. Если он просто вставляет / удаляет / обновляет, переписать, чтобы использовать отдельные команды. Если есть IF, посмотрите, могут ли они быть операторами CASE или условиями WHERE для вставок / удалений / обновлений. Если это так, удалите цикл и используйте команды set.

Я взял циклы и заменил их командами на основе набора и сократил время выполнения с минут до нескольких секунд. Я взял процедуры со многими вложенными циклами и вызовами процедур и сохранил циклы (было невозможно использовать только вставки / удаления / обновления), но я убрал курсор, и увидел меньше блокировок / блокировок и значительное повышение производительности. Вот два метода зацикливания, которые лучше, чем циклы курсора ...

если вам нужно выполнить цикл, над набором сделайте что-то вроде этого:

--this looks up each row for every iteration
DECLARE @msg VARCHAR(250)
DECLARE @hostname sysname

--first select of currsor free loop
SELECT @hostname= min(RTRIM(hostname))
    FROM  master.dbo.sysprocesses (NOLOCK)
    WHERE  hostname <> ''

WHILE @hostname is not null
BEGIN
    set @msg='exec master.dbo.xp_cmdshell "net send ' 
        + RTRIM(@hostname) + ' '
        + 'testing  "'
    print @msg
    --EXEC (@msg)

    --next select of cursor free loop
    SELECT @hostname= min(RTRIM(hostname))
        FROM master.dbo.sysprocesses (NOLOCK)
        WHERE  hostname <> ''
        and hostname > @hostname
END

если у вас есть разумный набор элементов (не 100 000) для циклического перебора, вы можете сделать это:

--this will capture each Key to loop over
DECLARE @msg VARCHAR(250)
DECLARE @From   int
DECLARE @To     int
CREATE TABLE #Rows
(
     RowID     int not null primary key identity(1,1)
    ,hostname  varchar(100)
)

INSERT INTO #Rows
SELECT DISTINCT hostname
    FROM  master.dbo.sysprocesses (NOLOCK)
    WHERE  hostname <> ''
SELECT @From=0,@[email protected]@ROWCOUNT

WHILE @From<@To
BEGIN
    SET @[email protected]+1

    SELECT @msg='exec master.dbo.xp_cmdshell "net send ' 
        + RTRIM(hostname) + ' '
        + 'testing  "'
        FROM #Rows WHERE [email protected]
    print @msg
    --EXEC (@msg)
END

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

Первоначально я делал это с помощью курсора, и с функциональной точки зрения это отвечало требованиям бизнеса. С технической точки зрения, это оказалось шоу-стопором, потому что по мере увеличения объема данных код занимал экспоненциально больше времени. Решение состояло в том, чтобы заменить курсор коррелированным подзапросом, который отвечал функциональным требованиям и устранял любые проблемы с производительностью.

Надеюсь это поможет,

Билл

Ваш ответ на вопрос