Во-первых, вам не нужен курсор. Нечто подобное следующему будет иметь ту же семантику (из начальной позиции, где все Event
равны NULL
) и будет более эффективным.
WITH T
AS (SELECT [Event],
RANK() OVER (PARTITION BY [Product], [Customer]
ORDER BY [Date] DESC) AS Rnk
FROM MyTable)
UPDATE T
SET [Event] = 'No Event'
WHERE Rnk > 1
Во-вторых, вопрос в заголовке о коммите в цикле курсора такой же, как и везде. Вам просто нужно заявление COMMIT
. Однако, если вы не выполняете это внутри более крупной транзакции, оператор UPDATE
все равно будет автоматически зафиксирован.
В-третьих, ваш настоящий вопрос, похоже, не о коммите. Речь идет о курсоре, отражающем обновления данных на последующих итерациях. Для рассматриваемого случая вам понадобится курсор DYNAMIC
Определяет курсор, который отражает все изменения данных, внесенные в строки в
его результат устанавливается при прокрутке курсора. Значения данных,
порядок и членство строк могут меняться при каждой выборке.
Не все запросы поддерживают динамические курсоры. Код в вопросе будет, но без ORDER BY
не определит, в каком порядке будут обрабатываться строки и, следовательно, будут ли вы видеть видимые результаты. Я добавил ORDER BY
и индекс для поддержки этого, чтобы можно было использовать динамический курсор.
Если вы попробуете следующее, вы увидите, что курсор выбирает только одну строку, поскольку даты обрабатываются в порядке убывания, а когда обрабатывается первая строка, таблица обновляется, так что больше строк не подходит для следующей выборки. Если вы закомментируете UPDATE
внутри цикла курсора, все три строки извлекаются.
CREATE TABLE MyTable
(
[Product] INT,
[Customer] INT,
[Date] DATETIME,
[Event] VARCHAR(10) NULL,
PRIMARY KEY ([Date], [Product], [Customer])
)
INSERT INTO MyTable
VALUES (1,1,'20081201',NULL),
(1,1,'20081202',NULL),
(1,1,'20081203',NULL)
DECLARE @Product INT,
@Customer INT,
@Date DATETIME,
@Event VARCHAR(10)
DECLARE cur CURSOR DYNAMIC TYPE_WARNING FOR
SELECT [Product],
[Customer],
[Date],
[Event]
FROM MyTable
WHERE [Event] IS NULL
ORDER BY [Date] DESC
OPEN cur
FETCH NEXT FROM cur INTO @Product, @Customer, @Date, @Event
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @Product,
@Customer,
@Date,
@Event
-- Now I update my Event value to 'No Event' for records whose date is less than @Date
UPDATE MyTable
SET [Event] = 'No Event'
WHERE [Product] = @Product
AND [Customer] = @Customer
AND [Date] < @Date
FETCH NEXT FROM cur INTO @Product, @Customer, @Date, @Event
END
CLOSE cur
DEALLOCATE cur
DROP TABLE MyTable