Что является альтернативой курсорам для цикла SQL - PullRequest
5 голосов
/ 25 марта 2011

Использование SQL 2005/2008

Мне нужно использовать курсор вперед, но я не хочу страдать от низкой производительности.Есть ли более быстрый способ зацикливания без использования курсоров?

Ответы [ 9 ]

4 голосов
/ 25 марта 2011

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

http://msdn.microsoft.com/en-us/library/ms178642.aspx

4 голосов
3 голосов
/ 25 марта 2011

Общие табличные выражения были бы хорошей альтернативой, как предложил @Neil. Вот пример из Adventureworks:

WITH cte_PO AS 
(
SELECT [LineTotal]
  ,[ModifiedDate]
FROM [AdventureWorks].[Purchasing].[PurchaseOrderDetail]
),
minmax AS
(
    SELECT MIN([LineTotal]) as DayMin
        ,MAX([LineTotal]) as DayMax
        ,[ModifiedDate]
    FROM cte_PO
    GROUP BY [ModifiedDate]
)
SELECT * FROM minmax ORDER BY ModifiedDate

Вот несколько строчек из того, что он возвращает:

DayMin     DayMax     ModifiedDate
135.36     8847.30    2001-05-24 00:00:00.000
129.8115   25334.925  2001-06-07 00:00:00.000
2 голосов
/ 25 марта 2011

Мне нужно использовать курсор вперед, но я не хочу страдать от низкой производительности.Есть ли более быстрый способ зацикливания без использования курсоров?

Это зависит от того, что вы делаете с курсором.

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

Однако есть некоторые вещи, в которых SQL Server просто не годится, например, вычисление совокупных значений или объединение в диапазонах дат.

Такие запросы можно выполнять быстрее, используя CURSOR:

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

Если вы отправили запрос, мы могли бы его оптимизировать и избавиться от CURSOR.

1 голос
/ 14 мая 2018

Вот пример с использованием курсора:

DECLARE @VisitorID int
DECLARE @FirstName varchar(30), @LastName varchar(30)
-- declare cursor called ActiveVisitorCursor 
DECLARE ActiveVisitorCursor Cursor FOR
SELECT VisitorID, FirstName, LastName 
FROM Visitors
WHERE Active = 1
-- Open the cursor
OPEN ActiveVisitorCursor 
-- Fetch the first row of the cursor and assign its values into variables
FETCH NEXT FROM ActiveVisitorCursor INTO @VisitorID, @FirstName, @LastName 
-- perform action whilst a row was found
WHILE @@FETCH_STATUS = 0
BEGIN
 Exec MyCallingStoredProc @VisitorID, @Forename, @Surname
 -- get next row of cursor
 FETCH NEXT FROM ActiveVisitorCursor INTO @VisitorID, @FirstName, @LastName 
END
 -- Close the cursor to release locks
CLOSE ActiveVisitorCursor 
 -- Free memory used by cursor
DEALLOCATE ActiveVisitorCursor 

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

/* Here is alternative approach */

-- Create a temporary table, note the IDENTITY
-- column that will be used to loop through
-- the rows of this table
CREATE TABLE #ActiveVisitors (
       RowID int IDENTITY(1, 1), 
       VisitorID int,
       FirstName varchar(30),
       LastName varchar(30)
 )
DECLARE @NumberRecords int, @RowCounter int
DECLARE @VisitorID int, @FirstName varchar(30), @LastName varchar(30)

-- Insert the resultset we want to loop through
-- into the temporary table
INSERT INTO #ActiveVisitors (VisitorID, FirstName, LastName)
SELECT VisitorID, FirstName, LastName
FROM Visitors
WHERE Active = 1 

-- Get the number of records in the temporary table
SET @NumberRecords = @@RowCount 
--You can use: SET @NumberRecords = SELECT COUNT(*) FROM #ActiveVisitors
SET @RowCounter = 1

-- loop through all records in the temporary table
-- using the WHILE loop construct
WHILE @RowCounter <= @NumberRecords
BEGIN
 SELECT @VisitorID = VisitorID, @FirstName = FirstName, @LastName = LastName 
 FROM #ActiveVisitors
 WHERE RowID = @RowCounter

 EXEC MyCallingStoredProc @VisitorID, @FirstName, @LastName

 SET @RowCounter = @RowCounter + 1
END

-- drop the temporary table
DROP TABLE #ActiveVisitors
1 голос
/ 25 марта 2011

Не используйте курсор, вместо этого ищите решение на основе множеств.Если вы не можете найти решение на основе множеств ... все равно не используйте курсор!Опубликуйте подробности того, чего вы пытаетесь достичь, кто-то сможет найти для вас решение на основе множеств.

1 голос
/ 25 марта 2011

В зависимости от того, для чего вы хотите, вы можете использовать таблицу подсчета.

У Джеффа Модена есть отличная статья о таблицах учета Здесь

1 голос
/ 25 марта 2011

Неверно говорить «Курсоры влияют на производительность SQL».У них, конечно, есть склонность, но многое из этого связано с тем, как люди их используют.

Первый вариант - попытаться найти подход к проблеме на основе множеств.

Еслилогически не существует подхода, основанного на множествах, и запрос для Cursor затрагивает реальные (не временные) таблицы, а затем использует ключевое слово STATIC, которое поместит результаты оператора SELECT в таблицу Temp и, следовательно, не заблокирует базуТаблицы запроса, когда вы просматриваете результаты.Использование STATIC не поможет, если вам нужно быть чувствительным к записям, которые могут исчезнуть при обработке результирующего набора, но это спорный вопрос, если вы рассматриваете возможность преобразования в цикл WHILE для временной таблицы (поскольку это также не приведет к изменениямк базовым данным).

0 голосов
/ 09 января 2015

В некоторых случаях можно использовать Таблицы подсчета . Это может быть хорошей альтернативой цикла и ошибок, но помните, что это не может быть применено в каждом случае. Хорошо объяснимый случай можно найти здесь

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