2012-05-31 3 views
3

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

У меня есть следующая таблица:

ID Constraint_Value 
---------------------------- 
1 (OldVal_1) (OldVal_2) 
2 (OldVal_2) (OldVal_1) 

... и я хочу использовать данные из следующей таблицы, чтобы сделать обновление:

oldValue newValue 
---------------------------- 
OldVal_1 NewVal_1 
OldVal_2 NewVal_2 

После обновления, я стремлюсь по следующим вопросам:

ID Constraint_Value 
---------------------------- 
1  (NewVal_1) (NewVal_2) 
2  (NewVal_2) (NewVal_1) 

Следующий SQL иллюстрирует мою проблему (которую вы можете запустить в SQL Management Stud ИО без какой-либо настройки):

IF OBJECT_ID('tempdb..#tmpConstraint') IS NOT NULL DROP TABLE #tmpConstraint 
GO 
CREATE TABLE tempdb..#tmpConstraint (constraint_id INT PRIMARY KEY, constraint_value varchar(256)) 
GO 

IF OBJECT_ID('tempdb..#tmpUpdates') IS NOT NULL DROP TABLE #tmpUpdates 
GO 
CREATE TABLE tempdb..#tmpUpdates (oldValue varchar(256), newValue varchar(256)) 
GO 

insert into #tmpConstraint 
values (1, '(OldVal_1) (OldVal_2)') 

insert into #tmpConstraint 
values (2, '(OldVal_2) (OldVal_1)') 

insert into #tmpUpdates 
values ('OldVal_1', 'NewVal_1') 

insert into #tmpUpdates 
values ('OldVal_2', 'NewVal_2') 

select * from #tmpConstraint 

update c 
set constraint_value = REPLACE(constraint_value, u.oldValue, u.newValue) 
from #tmpConstraint c 
cross join #tmpUpdates u 

select * from #tmpConstraint 

Это дает результаты:

(Before) 
1 (OldVal_1) (OldVal_2) 
2 (OldVal_2) (OldVal_1) 

(After) 
1 (NewVal_1) (OldVal_2) 
2 (OldVal_2) (NewVal_1) 

Как вы можете видеть только OldVal_1 был обновлен. OldVal_2 остался прежним.

Как обновить поле со всеми данными в таблице поиска?

+1

Я понимаю, что этот вопрос похож на другой вопрос, я спросил (http://stackoverflow.com/questions/10836092/how-to-update-a-table- based-on-a-xml-parameter), но я удалил XML из уравнения по этому вопросу, поэтому, надеюсь, это может привести к некоторым различным подходам. Конечно, я уточню этот вопрос с любыми полезными ответами из другого вопроса. –

+1

.. Многозначные столбцы, один из худших анти-шаблонов SQL, известных (для этого и по другим причинам). Пожалуйста, шпиндель, сверните и калечите оригинального дизайнера. Похоже, что некоторые версии SQL Server поддерживают CTE для операторов UPDATE, поддерживают ли они _recursive_ в этом случае? Если это так, вы, вероятно, можете написать CTE, чтобы собрать новое значение 'constraint_value' .... В противном случае, единственное, что я могу придумать, - это запустить оператор несколько раз, пока строка имеет экземпляр старого значения. –

+0

@ X-Zero - в качестве оригинального дизайнера я начну шпинделировать, сворачивать и калечить себя, как только закончу писать этот комментарий! Однако, чтобы дать вам некоторый контекст, фактическое значение в поле constraint_value является формулой (например, «(((100)/OldVal_1) * OldVal_2)»), что я должен обновить некоторые элементы (по причинам, слишком утомительным для объяснить). Как вы можете себе представить, формула не очень хорошо хранится в реляционной базе данных, особенно если эта формула может превратиться в более условный алгоритм в будущем. Его все еще анти-шаблон! –

ответ

1

UPDATE влияет только на каждую строку источника один раз. Поэтому самый простой обходной путь, о котором я знаю, - это курсор.

DECLARE @o VARCHAR(256), @n VARCHAR(256); 

DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY 
FOR SELECT oldValue, newValue FROM #tmpUpdates; 

OPEN c; 

FETCH c INTO @o, @n; 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    UPDATE #tmpConstraint 
     -- note full match only: 
     SET constraint_value = REPLACE(constraint_value, '(' + @o + ')', '(' + @n + ')') 
     -- and note we only touch rows where there is a full match: 
     WHERE constraint_value LIKE '%(' + @o + ')%'; 

    FETCH c INTO @o, @n; 
END 

CLOSE c; 
DEALLOCATE c; 

SELECT constraint_id, constraint_value FROM #tmpConstraint; 

Результаты:

constraint_id constraint_value 
------------- --------------------- 
1    (NewVal_1) (NewVal_2) 
2    (NewVal_2) (NewVal_1) 
+1

Или вы можете пропустить курсор, добавить предложение WHERE в исходное обновление и использовать WHILE @@ ROWCOUNT> 0 (это также будет обрабатывать каскадные изменения, где NewVal_n = OldVal_m, и вы хотите, чтобы OldVal_n закончил как NewVal_m). – GilM

+1

@GilM yes true, это сработает, но я думаю, что явный курсор немного более прост (даже если это немного больше кода) и не должен выполняться иначе. Ваша вторая мысль потребует очень тщательного планирования, чтобы убедиться, что каскад обработан в правильном порядке (что было бы легче гарантировать с помощью курсора). –

+1

С решением @@ ROWCOUNT порядок не имеет значения (но циклы были бы возможны, так что это нужно было бы обработать). Но я не религиозен. Обработка строк в ряд проще понять, и это хорошо. Я в основном смирился с использованием курсоров иногда (они были источником многих проблем обслуживания в старые времена из-за неаккуратного кодирования и ошибок ядра sql, так что я все еще немного издеваюсь над ними). WHILE @@ ROWCOUNT> 0 был эффективным способом прохождения произвольно глубоких иерархий до CTE, поэтому у меня все еще есть мягкое пятно для этого шаблона. – GilM

Смежные вопросы