Каковы различные способы замены курсора? - PullRequest
5 голосов
/ 09 июня 2009

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

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

Ответы [ 4 ]

7 голосов
/ 09 июня 2009

стараться никогда не зацикливаться, работать с наборами данных.

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

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,@To=@@ROWCOUNT

WHILE @From<@To
BEGIN
    SET @From=@From+1

    SELECT @msg='exec master.dbo.xp_cmdshell "net send ' 
        + RTRIM(hostname) + ' '
        + 'testing  "'
        FROM #Rows WHERE RowID=@From
    print @msg
    --EXEC (@msg)
END
4 голосов
/ 09 июня 2009

Я заменил некоторые курсоры на циклы 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'
2 голосов
/ 09 июня 2009

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

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

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

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

Марк

0 голосов
/ 09 июня 2009

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

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

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

Bill

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