T-SQL 字符串替换未锁定 - 最后更新获胜
T-SQL String Replace is Not Locking - Last Update Wins
我有以下存储过程,它旨在循环访问一个字符串列表,其中包含多个 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
为什么第一个版本失败了?我该如何解决?
不确定为什么要使整个过程如此复杂。可能是我没有清楚地理解这个要求。据我了解,您希望仅更新 table 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)
您遇到的问题是 "undefined" 当 UPDATE FROM JOIN
有多个匹配项时的行为。
为了使您的更新成为可能,您应该运行它多次一次更新一对值,正如您在第二个代码演示中所建议的那样。
相关:How is this script updating table when using LEFT JOINs? and Let’s deprecate UPDATE FROM!:
SQL Server will happily update the same row over and over again if it matches more than one row in the joined table, >>with only the result of the last of those updates sticking<<.
我有以下存储过程,它旨在循环访问一个字符串列表,其中包含多个 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
为什么第一个版本失败了?我该如何解决?
不确定为什么要使整个过程如此复杂。可能是我没有清楚地理解这个要求。据我了解,您希望仅更新 table 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)
您遇到的问题是 "undefined" 当 UPDATE FROM JOIN
有多个匹配项时的行为。
为了使您的更新成为可能,您应该运行它多次一次更新一对值,正如您在第二个代码演示中所建议的那样。
相关:How is this script updating table when using LEFT JOINs? and Let’s deprecate UPDATE FROM!:
SQL Server will happily update the same row over and over again if it matches more than one row in the joined table, >>with only the result of the last of those updates sticking<<.