Курсор говорит, что только для чтения, хотя я объявил это "для обновления" - PullRequest
3 голосов
/ 13 декабря 2011

Я пытаюсь обновить строку внутри курсора. Я пытаюсь обновить цепочку записей с помощью OLD_QTY и NEW_QTY. Однако, когда я пытаюсь выполнить обновление, выдается ошибка The cursor is READ ONLY, хотя я включил for update of OLD_QTY, NEW_QTY в мое объявление. Не имеет значения, если я включу OLD_QTY и NEW_QTY в оператор выбора.

declare @current_inv_guid uniqueidentifier
declare @last_inv_guid uniqueidentifier
declare @current_vid int
declare @last_vid int
--declare @current_new_qty money
declare @last_new_qty money
--declare @current_old_qty money

declare iaCursor cursor 
      for select INV_GUID, old_VID
          --, OLD_QTY, NEW_QTY  
          from #IA 
          order by INV_GUID, old_vid, ENTRY_NUM 
      for update --of OLD_QTY, NEW_QTY
open iaCursor
Fetch next from iaCursor into @current_inv_guid, @current_vid --, @current_old_qty, @current_new_qty

while @@fetch_status = 0
begin
    --test to see if we hit a new chain.
    if(@last_inv_guid <> @current_inv_guid or @current_vid <> @last_vid)
    begin
        set @last_new_QTY = (select #lots.QTY_RECEIVED from #lots where #lots.INV_GUID = @current_inv_guid and LOT_VID = @current_vid)
        set @last_inv_guid = @current_inv_guid
        set @last_vid = @current_vid    
    end

    --update the current link in the chain
    update #ia
        set OLD_QTY = @last_new_QTY,
            NEW_QTY = @last_new_QTY + QTY_CHANGE,
            @last_new_QTY = @last_new_QTY + QTY_CHANGE
        where current of iaCursor

    --get the next link
    fetch next from iaCursor into @current_inv_guid, @current_vid --, @current_old_qty, @current_new_qty
end 

close iaCursor
deallocate iaCursor

Ответы [ 6 ]

7 голосов
/ 13 декабря 2011

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

Прекрасно использовать order by в обновляемом курсоре, но вы должны быть более многословны и сообщать SQL Server, что вам нужно, например:

declare iaCursor cursor
local
forward_only
keyset
scroll_locks
for
  select INV_GUID, old_VID
  from #IA 
  order by INV_GUID, old_vid, ENTRY_NUM 
for update of OLD_QTY, NEW_QTY
7 голосов
/ 13 декабря 2011

Помещение order by в select делает курсор доступным только для чтения.

4 голосов
/ 26 июля 2016

На странице документация есть импортная, но тонкая заметка, которую перечислил Патрик:

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

И, конечно же, курсоры STATIC доступны только для чтения.

2 голосов
/ 13 декабря 2011

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

Я не положительный , так как я не знаю ваш дизайн таблицы, но я считаю, что должно работать следующее.Вы можете получить от этого лучшую производительность.В частности, я предполагаю, что QTY_CHANGE исходит от #ia, хотя это может быть и не так.

UPDATE #ia as a set (OLD_QTY, NEW_QTY) = (SELECT #lots.QTY_RECEIVED + (COUNT(b.*) * a.QTY_CHANGE), 
                                                 #lots.QTY_RECEIVED + ((COUNT(b.*) + 1) * a.QTY_CHANGE)
                                          FROM #lots
                                          LEFT JOIN #ia as b
                                          ON b.INV_GUID = a.INV_GUID
                                          AND b.OLD_VID = a.OLD_VID
                                          AND b.ENTRY_NUM < a.ENTRY_NUM
                                          WHERE #lots.INV_GUID = a.INV_GUID
                                          AND #lots.LOT_VID = a.OLD_VID)
WHERE EXISTS (SELECT '1'
              FROM #lots
              WHERE #lots.INV_GUID = a.INV_GUID
              AND #lots.LOT_VID = a.OLD_VID)

EDIT:

... предыдущая версия ответа была написана с точки зрения DB2, хотя в противном случае она была бы независимой от БД.Также возникла проблема использования одинакового значения QTY_CHANGE для каждой строки, что маловероятно.Это должна быть более идиоматическая версия SQL Server 2008, с большей вероятностью вывести правильный ответ:

WITH RT AS (SELECT #IA.inv_guid, #IA.old_vid, #IA.entry_num,
                   COALESCE(MAX(#Lots.qty_received), 0) + 
                         SUM(#IA.qty_change) OVER(PARTITION BY #IA.inv_guid, #IA.old_vid 
                                                  ORDER BY #IA.entry_num) 
                        AS running_total                                      
                       FROM #IA
                       LEFT JOIN #Lots
                              ON #Lots.inv_guid = #IA.inv_guid
                                 AND #Lots.lot_vid = #IA.old_vid)
UPDATE #IA
SET #IA.old_qty = RT.running_total - #IA.qty_change, #IA.new_qty = RT.running_total
FROM #IA
JOIN RT
  ON RT.inv_guid = #IA.inv_guid
     AND RT.old_vid = #IA.old_vid
     AND RT.entry_num = #IA.entry_num
0 голосов
/ 12 июня 2018

Используйте предложение DYNAMIC, найденное в документации .

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

0 голосов
/ 02 сентября 2014

Некоторые объявления курсора не разрешают обновления. Документация дает подсказку в следующем замечании:

  • Если оператор SELECT не поддерживает обновления (недостаточные разрешения, доступ к удаленным таблицам, которые не поддерживают обновления, и и так далее), курсор READ_ONLY.

Я столкнулся с той же проблемой при попытке присоединить «вставленный» объект триггера в операторе select объявления курсора.

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