尽管我宣称它为“更新”,Cursor仍然只读它

时间:2011-12-12 20:56:43

标签: sql sql-server sql-server-2008

我正在尝试更新游标内的一行。我要做的是用OLD_QTY和NEW_QTY更新记录链。但是,当我尝试进行更新时,即使我在我的declration中包含The cursor is READ ONLY,也会出现错误for update of OLD_QTY, NEW_QTY。如果我在select语句中包含OLD_QTYNEW_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 个答案:

答案 0 :(得分:7)

order by中放置select使光标只读。

答案 1 :(得分:7)

您没有明确说明您想要的行为,因此,default rules适用,根据这些行为,游标可能是可更新的,也可能不是可更新的,具体取决于基础查询。

在可更新的游标中使用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

答案 2 :(得分:4)

Patrick列出的documentation页面上有一个导入但微妙的注释:

  

如果查询引用了至少一个没有唯一索引的表,那么   keyset cursor转换为静态游标。

当然STATIC游标是只读的。

答案 3 :(得分:2)

除了你在答案中提到的原因之外,你正在做的事情与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)

<击>


编辑:

...答案的先前版本是用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

答案 4 :(得分:0)

某些游标声明不允许更新。 documentation在以下注释中给出了提示:

  
      
  • 如果SELECT语句不支持更新(权限不足,访问不支持更新的远程表,和   所以),光标是READ_ONLY。
  •   

我试图加入&#34;插入&#34;时遇到了同样的问题。游标声明的select语句中的触发器的对象。

答案 5 :(得分:0)

使用documentation中的DYNAMIC子句。

  

定义一个游标,该游标反映在滚动游标时对其结果集中的行所做的所有数据更改。每次获取时,行的数据值,顺序和成员资格都会发生变化。