我有以下存储过程,该存储过程旨在遍历字符串列表,其中包含几个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
为什么第一个版本失败?我该如何解决?
答案 0 :(得分:1)
当UPDATE FROM JOIN
可能存在多个匹配项时,您遇到的问题是“未定义”行为。
为了使更新成为可能,您应该多次运行它,如第二个代码演示中所建议的那样一次更新一对值。
相关:How is this script updating table when using LEFT JOINs?和Let’s deprecate UPDATE FROM!:
如果SQL Server与联接表中的多行匹配,SQL Server将愉快地一次又一次地更新同一行,而>>仅保留那些更新的最后结果<<。
答案 1 :(得分:0)
不确定为什么要使整个过程变得如此复杂。可能是我不清楚要求。根据我的理解,您希望仅更新表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)