【问题标题】:T-SQL String Replace is Not Locking - Last Update WinsT-SQL 字符串替换未锁定 - 最后更新获胜
【发布时间】:2019-06-20 16:24:45
【问题描述】:

我有以下存储过程,它旨在遍历字符串列表,其中包含几个prefix.bucketName 形式的子字符串。我想遍历每个字符串和每个桶名,并用新前缀替换旧前缀,但保持相同的桶名。

举个例子,考虑这个原始字符串:

"(OldPrefix.BucketA)(OldPrefix.BucketB)"

所以例如我想得到:

"(NewPrefix.BucketA)(NewPrefix.BucketB)"

实际上得到的是这样的:

"(OldPrefix.BucketA)(NewPrefix.BucketB)"

因此,一般来说,只有一个前缀会被更新,并且无法预测是哪一个。根据我所做的一些调查,似乎两个替换都确实有效,但实际上只有最后一个被保存了。似乎 SQL 应该锁定此列,但同时读取两者,应用替换,然后写入两者,将最后一次写入保留为列中显示的内容。

这里是查询 - 为了隐私,所有变量名称都已更改 - 为简洁起见,省略了一些错误处理和数据验证代码:

DECLARE @PrefixID INT                   = 1478,
DECLARE @PrefixName_OLD NVARCHAR(50)    = 'OldPrefix',
DECLARE @PrefixName_NEW NVARCHAR(50)    = 'NewPrefix'

BEGIN TRAN 
    -- Code to rename the section itself here not shown for brevity

    UPDATE 
        dbo.Component 
    SET
        AString= REPLACE(AString,'('+@Prefix_OLD+'.'+b.BucketName+')', '('+@PrefixName_NEW+'.'+b.BucketName+')'),
    FROM
        dbo.Component sc
    JOIN
        dbo.ComponentBucketFilterInString  fis
    ON
        sc.ComponentID = fis.ComponentID
    JOIN
        dbo.Buckets b
    ON
        fis.BucketID = b.BucketID   
    WHERE
        b.PrefixID = @PrefixID
COMMIT
RETURN 1

当我使用 while 循环编写相同的查询时,它按预期执行:

DECLARE @BucketsToUpdate TABLE 
(
    BucketID INT,
    BucketName VARCHAR(256)
)

INSERT INTO @BucketsToUpdate
SELECT BucketID, BucketName
FROM Buckets WHERE PrefixID = @PrefixID

WHILE EXISTS(SELECT 1 FROM @BucketsToUpdate)
BEGIN
    DECLARE @currentBucketID INT,
            @currentBucketName VARCHAR(256)
    SELECT TOP 1 @currentBucketID = bucketID, @currentBucketName = bucketName FROM @BucketsToUpdate 
    UPDATE 
            dbo.Component 
        SET
            AString = REPLACE(AString,'('+@PrefixName_OLD+'.'+@currentBucketName+')', '('+@PrefixName_NEW+'.'+@currentBucketName+')')
        FROM
            dbo.Component  sc
        JOIN
            dbo.ComponentBucketFilterInString  fis
        ON
            sc.ComponentID = fis.ComponentID
        WHERE fis.BucketID = @currentBucketID
    DELETE FROM @BucketsToUpdate WHERE BucketID = @currentBucketID
END

为什么第一个版本失败了?我该如何解决?

【问题讨论】:

  • 请发布示例数据dbfiddle.uk 问题很可能是UPDATE FROM JOIN 当多行匹配同一行以更新您有未定义的行为 - 最后更新获胜跨度>
  • 会的,请稍等。
  • @LukaszSzozda 这应该可以满足您的需求dbfiddle.uk/…
  • @Lukasz Szozda 我不确定,但根据您的评论,您发布的内容似乎是基于我所经历的唯一解释。愿意留下这个答案,以便我接受吗?

标签: sql sql-server tsql sql-update locking


【解决方案1】:

UPDATE FROM JOIN 有多个匹配项时,您遇到的问题是“未定义”行为。

为了使您的更新成为可能,您应该多次运行它,一次更新一对值,正如您在第二个代码演示中所建议的那样。

相关:How is this script updating table when using LEFT JOINs?Let’s deprecate UPDATE FROM!

如果同一行匹配连接表中的多行,SQL Server 会愉快地一次又一次地更新,>>只有最后一次更新的结果仍然存在

【讨论】:

    【解决方案2】:

    不知道你为什么要让整个过程如此复杂。可能是我没有清楚地理解要求。根据我的理解,您希望仅更新表 dbo.Component 中列“AString”的前缀部分。当前值例如是-

    (OldPrefix.BucketA)(OldPrefix.BucketB)
    

    您想将值更新为-

    (NewPrefix.BucketA)(NewPrefix.BucketB)
    

    我说的对吗?如果是,您可以使用如下简单的更新脚本更新所有记录-

    DECLARE @PrefixID INT                   = 1478
    DECLARE @PrefixName_OLD NVARCHAR(50)    = 'OldPrefix'
    DECLARE @PrefixName_NEW NVARCHAR(50)    = 'NewPrefix'
    
    UPDATE  Component 
    SET AString= REPLACE(AString,@PrefixName_OLD,@PrefixName_NEW)   
    

    【讨论】:

    • 好吧,我已经简化了给出的代码和示例以使问题更容易理解,但实际上并不是那么简单。这些字符串中有很多特殊的语法,我需要注意不要替换不紧跟bucketname的前缀实例。
    • 只显示一些示例数据和您的预期输出。
    • 我不能对这些数据过于具体,因为它归我公司所有。请相信当我说由于我无法指定的原因时,我需要以类似于我指定的方式进行替换。
    • 那么您在使用我的简单更新脚本时遇到了什么问题?
    猜你喜欢
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 2018-08-03
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多