Замена строки T-SQL не блокируется - последнее обновление выигрывает - PullRequest
1 голос
/ 20 июня 2019

У меня есть следующая хранимая процедура, предназначенная для итерации по списку строк, который содержит несколько подстрок формы 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

Почему отказывает первая версия?Как я могу это исправить?

Ответы [ 2 ]

1 голос
/ 24 июня 2019

Проблема, с которой вы сталкиваетесь, заключается в "неопределенном" поведении, когда для UPDATE FROM JOIN возможно более одного совпадения.

Чтобы сделать возможным обновление, вы должны запустить его несколько раз, обновляя одну пару значенийв то время, как вы предложили во втором демонстрационном коде.

Связано: Как эта таблица обновления скрипта при использовании LEFT JOINs? и Давайте не будем использовать UPDATE FROM! :

SQL Server будет успешно обновлять одну и ту же строку снова и снова, если она соответствует более чем одной строке в объединенной таблице, >> только с последним последним прикреплением последнего из этих обновлений <<. </strong>

0 голосов
/ 21 июня 2019

Не уверен, почему вы делаете весь процесс таким сложным.Может быть, я не совсем понимаю требование.Насколько я понимаю, вы хотите обновить только часть префикса для столбца 'AString' в таблице dbo.Component.Например, текущее значение -

(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)   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...