【问题标题】:FOR UPDATE cannot be specified on a READ ONLY cursor不能在只读游标上指定 FOR UPDATE
【发布时间】:2011-12-13 19:52:34
【问题描述】:

我正在使用游标更新表中的单个字段,并且尝试在选择文本中使用 ORDER BY 声明游标。

我有以下示例数据:

testTable:
RecordGuid RecordID DupeParentID
---------- -------- ------------
[guid]     A        Y
[guid]     A        N
[guid]     A        N
[guid]     A        N
[guid]     B        Y
[guid]     B        N
[guid]     B        N
[guid]     C        Y
[guid]     C        N
[guid]     C        N

还有脚本:

DECLARE @allcounter INT
SET @allcounter = 1

SELECT RecordID, count(*) as [NumberDupes]
INTO #RecordGroupCounts
FROM testTable
GROUP BY RecordID

DECLARE @temp VARCHAR(500)
DECLARE @current VARCHAR(500)

DECLARE c1 CURSOR 
FOR 
    SELECT RecordID FROM testTable WHERE RecordID IN (SELECT RecordID FROM testTable WHERE DupeParentID = 'Y') 
    ORDER BY RecordID
FOR UPDATE OF RecordID

OPEN c1

FETCH NEXT FROM c1 INTO @current
FETCH NEXT FROM c1 INTO @current

WHILE @@fetch_status = 0
BEGIN
    UPDATE testTable
    SET RecordID = RecordID + '-' + cast(@allcounter AS VARCHAR)
    WHERE CURRENT OF c1

    IF (@allcounter + 1) = (SELECT [NumberDupes] FROM #RecordGroupCounts WHERE RecordID = @current)
    BEGIN
        FETCH NEXT FROM c1 INTO @current
        SET @allcounter = 0
    END 
    SET @allcounter = @allcounter + 1

    FETCH NEXT FROM c1 INTO @current
END
CLOSE c1
DEALLOCATE c1

所有这些的期望输出是:

RecordGuid RecordID DupeParentID
---------- -------- ------------
[guid]     A        Y
[guid]     A-1      N
[guid]     A-2      N
[guid]     A-3      N
[guid]     B        Y
[guid]     B-1      N
[guid]     B-2      N
[guid]     C        Y
[guid]     C-1      N
[guid]     C-2      N

我正在使用 SQL Server 2000,所以我没有可用的 ROW_NUMBER() - 我知道执行此操作的常用方法是使用循环,但我绝不是 DBA,如果我删除,这目前可以工作我在游标声明中的ORDER BY RecordID

与我当前的测试表一样小,这似乎工作正常,但我尝试订购它的原因是我相当确定如果 RecordID 不按顺序它会中断(通过RecordID ASC, DupeParentID DESC) 我打算半定期地在更大的记录集上使用它。有没有办法定义更新游标的顺序?光标是否以某种方式自动排序?如果没有,是否有更简单(或更快)的方法来为 SQL Server 2000 编写此代码?

【问题讨论】:

  • 我不确定我是否看到 record_id 上缺少 order by 会破坏这一点。尽管看起来将重复项标记为 -1 -2 -3 等的顺序有点随意。更快的编写方法是缺少循环并将其转换为基于集合的东西,但这可能具有挑战性。作为一个完整的旁注,您的 c1 游标的填充可以重做 SELECT RecordID FROM testTable WHERE DupeParentID = 'Y' group by recordID(该组将消除重复项,我怀疑这是您的目标)。跨度>
  • @Tewlfth - 目前(按顺序),我收到指定的错误消息。没有它,经过一些测试,它会运行,但似乎光标根本没有对数据进行排序,因此我的计数器更新了错误数字的记录(我得到了类似A, A-1, A-2, C-3, A, C-1, C-2...)。至于DupeParentID = 'Y',我需要每条RecordID 为DupeParentID = 'Y' 的记录(对于游标本身)——不仅是“Y”记录,而且所有记录都将RecordID 与Y 匹配。
  • 希望其他人能够回答这个问题......非唯一数据和光标的混合非常残酷。介意我尝试不同的角度吗?如果我错了,请纠正我,但您目前在表格中有数据,并且此脚本被用作一次性更正(IE,您不打算在正在进行的产品中使用此逻辑)...如果那是这个案子,我确实有另一个想法让你处理。他们是否在“可测试”中区分 3 个记录 ID A Dupeparent_id N,或者您实际上是否有 3 个相同的记录来编号?
  • @Tewlfth 所以事实证明我忘记了一条关键信息 - 有一个主键,其中包含每个记录的唯一标识符(我只是没有包括它,因为它不直接相关我是如何更新的)。我会更新以反映这一点......
  • 我的假设对吗?我将组合一个更基于集合的解决方案(有点需要循环,但应该表现得更好。更新部分需要指南(我假设是大整数)......将开始写作

标签: sql sql-server tsql sql-server-2000


【解决方案1】:
select recordid, max(recordguid)
from testable
where DupeParentID = 'N'
group by recordID

上述语句应为每个记录 ID 返回 1 行,并带有该记录 ID 的 max(guID)。呵呵,拼写检查不断更改 guid 来指导。现在我们可以用 1 递增所有这些。

update testtable 
set recordID = recordID + '-1'
where recordguid in (select recordguid from (    select recordid, max(recordguid) recordguid
from testable
where DupeParentID = 'N'
group by recordID) a)

这里的逻辑?我们正在做的是使用 max recordguid 作为“第一个”的标识符来获取第一个重复 ID...lil 任意的,我们可以使用 min 以及只要它为每个 recordID 返回一个 guiID。如果您有其他关于将哪个记录称为 -1 与 -2 的逻辑,您可以将其包含在此处。

这将创建所有的 recordID-1 (A-1,B-1) 并保留其余部分。现在你应该能够循环这个并根据需要增加 -1 ......或者你可以重复运行它手动增加 -1 如果这是一次性修复......你的电话在那里。

让我知道它是如何进行的......逻辑应该可以工作,并且比一次通过每个记录ID要快得多。

【讨论】:

  • 有趣的想法 - 使用它我想我可以重写它以仅使用单个 while 循环。这将针对具有数十万条记录的表在生产中运行(尽管可能只有数百条记录需要更新),所以我试图保持它非常精简。我会在今晚/明天晚些时候试一试,然后告诉你进展如何。
  • 如果有帮助的话,几十万对这个有点小:) 如果你愿意,你可以设置一个变量来表示你在循环之前必须经历的最大重复次数.选择 max(rowcount) from (select recordID,count(1) as rowcount from testtable where duplicateparentID = 'N' group by recordID)a。这将动态分配您的查询需要循环的重复次数。
【解决方案2】:

您可以尝试以下方法。虽然我不希望性能非常好,但如果您需要定期在生产环境中运行查询,我不会推荐此解决方案。

declare @counter int ,@continue tinyint
set @counter =1 
set @continue = 1
while @continue = 1
Begin 
    update testtable 
    set testtable.recordid = testtable.recordid + ' - ' + CAST (@counter as nchar(6))
    from 
    testtable 
    inner join
    (  
    select  MAX(cast(t1.guid as char(36)))  as maxguid from testtable t1
    inner join testtable t2 on t1.recordid = t2.recordid 
    where
    t2.dupeparentid = 'y' and  t1.dupeparentid = 'n'
    group by t1.recordid
    ) t4

    on testtable.guid = cast (t4.maxguid as uniqueidentifier)

    if @@ROWCOUNT = 0 
    set @continue = 0
    set @counter = @counter + 1
end         

【讨论】:

    猜你喜欢
    • 2020-06-16
    • 1970-01-01
    • 1970-01-01
    • 2012-10-26
    • 2017-02-03
    • 1970-01-01
    • 2014-03-10
    • 1970-01-01
    • 2014-11-28
    相关资源
    最近更新 更多