【发布时间】: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