2012-05-31 5 views
2

테이블이 있으며 XML 매개 변수의 값을 기반으로 varchar 필드 중 하나를 업데이트하려고합니다.XML 매개 변수를 기반으로 테이블을 업데이트하는 방법

나는 다음과 같은 테이블이 있습니다

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

을하고 난 Constraint_Value 필드 업데이트하려면 다음 XML을 사용하려면 : 나는 다음과 같은 목표로하고 업데이트 후

<qaUpdates> 
    <qaUpdate><old>OldVal_1</old><new>NewVal_1</new></qaUpdate> 
    <qaUpdate><old>OldVal_2</old><new>NewVal_2</new></qaUpdate> 
</qaUpdates> 

을 :

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

다음 SQL은 (SQL Manag에서 실행할 수있는) 그냥 OldVal_1가 업데이트되었습니다 볼 수 있듯이

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

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

: 장담 스튜디오 어떤 설정하지 않고는) :

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 

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

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

select * from #tmpConstraint 

declare @myXML XML 
set @myXML = N'<qaUpdates> 
    <qaUpdate><old>OldVal_1</old><new>NewVal_1</new></qaUpdate> 
    <qaUpdate><old>OldVal_2</old><new>NewVal_2</new></qaUpdate> 
</qaUpdates>' 

update c 
set constraint_value = REPLACE(constraint_value, Child.value('(old)[1]', 'varchar(50)'), Child.value('(new)[1]', 'varchar(50)')) 
from #tmpConstraint c 
cross join @myXML.nodes('/qaUpdates/qaUpdate') as N(Child) 

select * from #tmpConstraint 

이 결과를 제공합니다. OldVal_2은 그대로 유지됩니다.

xml 매개 변수에 지정된 모든 요소로 필드를 업데이트하려면 어떻게해야합니까?

답변

2

재귀 cte를 사용하면 찾고있는 결과를 얻을 수 있습니다. 다음과 같이 보여줍니다. 하지만 적어도 커서/while-loop가 아닙니다.)

declare @tmpConstraint table (ID int , Constraint_Value varchar(256)) 
insert into @tmpConstraint values 
(1, '(OldVal_1) (OldVal_2)'), 
(2, '(OldVal_2) (OldVal_1)') 

declare @myXML XML 
set @myXML = N'<qaUpdates> 
    <qaUpdate><old>OldVal_1</old><new>NewVal_1</new></qaUpdate> 
    <qaUpdate><old>OldVal_2</old><new>NewVal_2</new></qaUpdate> 
</qaUpdates>' 

declare @xmlData table (oldValue varchar(256), newValue varchar(256)) 
insert into @xmlData 
select 
    oldValue = Child.value('(old)[1]', 'varchar(50)'), 
    newValue = Child.value('(new)[1]', 'varchar(50)') 
from @myXML.nodes('/qaUpdates/qaUpdate') as N(Child) 

위의 내용은 다음과 같이 설정되었습니다.

;with cte (ID, Constraint_Value, CLevel) 
as 
(
    select c.ID, c.Constraint_Value, 1 
    from @tmpConstraint c 

    union all 

    select p.ID, cast(replace(p.Constraint_Value, x.oldValue, x.newValue) as varchar(256)), p.CLevel + 1 
    from cte p 
    join @xmlData x on p.Constraint_Value like '%' + x.oldValue + '%' 
) 
update c 
set c.Constraint_Value = t.Constraint_Value 
from @tmpConstraint c 
join (
    select 
     *, 
     rn = row_number() over (partition by ID order by CLevel desc) 
    from cte 
) t on t.ID = c.ID and rn = 1 

select * from @tmpConstraint 
+0

좋은 답변, @mouters. cte를 사용하는 것은 매우 영리한 해결책입니다. 감사. –

+0

기꺼이 도와 드릴 수는 없겠지만 –

+0

이것은 훌륭한 솔루션입니다.하지만 실제로 CTE에 숨겨진 루프입니다. 또한 CTE는 원본 테이블보다 더 많은 행을 가질 수 있습니다 (n 개의 일치하는 값을 가진 각 행에 대해 1 + n + n (n-1) + n (n-1) 2) + ... n! 또한 기본 OPTION (MAXRECURSION n) 값은 100입니다. 그러나 행마다 일치하는 항목이 거의없는 적당한 크기의 테이블의 경우에는 마음에 들었습니다. – GilM

1

여기서 문제는 XML과 관련이 없다고 생각합니다. 하나의 UPDATE는 조인 된 행 수에 관계없이 각 행을 한 번만 업데이트한다는 것입니다. 난 당신이 추가 할 수 있다고 생각 절과 WHILE 루프가 대체 모두를 구할 수있는 곳은 A :

WHILE @@ROWCOUNT>0 
BEGIN 
    update c 
    set constraint_value = REPLACE(constraint_value, Child.value('(old)[1]', 'varchar(50)'), Child.value('(new)[1]', 'varchar(50)')) 
    from #tmpConstraint c 
    cross join @myXML.nodes('/qaUpdates/qaUpdate') as N(Child) 
    WHERE constraint_value LIKE '%' + Child.value('(old)[1]', 'varchar(50)') + '%' 
END 

그냥이 설정 @@의 RowCount 성명을 다음과 있는지 확인하십시오.

0

나는 이것이 이미 답변되었지만 cte를 사용하지 않고 이것을 할 수있는 방법이 있는지 궁금해했다. 어쨌든 더 큰 문제는 실제로 동일한 열/행에 2 개의 데이터를 저장한다는 것입니다. 이는 단일 업데이트 문 내에서 동일한 행을 두 번 업데이트 할 수 없다는 사실과 관련하여 문제를 일으키는 것입니다. 어쨌든 내 접근 (I 복잡성 시간 앞서 사과)이이었다 : 그것이 당신은 거기에 몇 가지 UDF의의가 있다면 정리 될 수있는 것보다

DECLARE @tmpConstraint TABLE (
    constraint_id INT PRIMARY KEY 
    ,constraint_value VARCHAR(256) 
    ) 

INSERT INTO @tmpConstraint 
VALUES (
    1 
    ,'(OldVal_1) (OldVal_2)' 
    ) 

INSERT INTO @tmpConstraint 
VALUES (
    2 
    ,'(OldVal_2) (OldVal_1)' 
    ) 

INSERT INTO @tmpConstraint 
VALUES (
    3 
    ,'(OldVal_3) (OldVal_21) (OldVal_1)' 
    ) 

DECLARE @myXML XML 

SET @myXML = N'<qaUpdates>  <qaUpdate><old>OldVal_1</old><new>NewVal_1</new></qaUpdate>  <qaUpdate><old>OldVal_2</old><new>NewVal_2</new></qaUpdate> </qaUpdates>' 

SELECT * 
FROM @tmpConstraint 

UPDATE C 
SET constraint_value = c.New_Val 
FROM (
    SELECT Constraint_ID UpdID 
     ,Constraint_value 
     ,STUFF((
       SELECT (' ' + New_value) 
       FROM (
        --Converts XML into a Table effectively splitting the string 
        SELECT constraint_id 
         ,t.value('.', 'varchar(200)') Current_value 
         ,Coalesce(Nullif('(' + new + ')', '()'), t.value('.', 'varchar(200)')) New_Value 
        FROM 
         --Converts single column into an xml document to split rows. Uses a blank space as the identifer of rows 
         (
         SELECT constraint_id 
          ,convert(XML, ('<R>' + replace(constraint_value, ' ', '</R><R>')) + '</R>') xmldoc 
         FROM @tmpConstraint 
         ) AS a 
        CROSS APPLY a.xmldoc.nodes('./R') AS b(t) 
        --Join to table containing proposed changes based on value to change    
        LEFT JOIN (
         SELECT Child.value('./old[1]', 'varchar(100)') old 
          ,Child.value('./new[1]', 'varchar(100)') new 
         FROM @myXML.nodes('/qaUpdates/qaUpdate') AS N(Child 
         ) 
        ) q2 ON '(' + old + ')' = t.value('.', 'varchar(200)') 
       ) Modified WHERE Modified.constraint_id = base.constraint_id FOR XML path('')) 
     ,1,1,'') New_Val 
FROM @tmpConstraint Base) c 

SELECT * 
FROM @tmpConstraint 

그것은 많이 지저분 보인다. 하지만 근본적으로 다중 값 열을 여러 행으로 나누었습니다. 나는 XML 파일을 사용하여이

1 (OldVal_1) 
1 (OldVal_2) 
2 (OldVal_2) 
2 (OldVal_1) 
3 (OldVal_3) 
3 (OldVal_21) 
3 (OldVal_1) 

에 동일이

1 (OldVal_1) (OldVal_2) 
2 (OldVal_2) (OldVal_1) 
3 (OldVal_3) (OldVal_21) (OldVal_1) 

을 켜기.이

OldVal_1 NewVal_1 
OldVal_2 NewVal_2 

같이 설정된 키 데이터 쌍으로 돌리면

1 (OldVal_1) (NewVal_1) 
1 (OldVal_2) (NewVal_2) 
2 (OldVal_2) (NewVal_2) 
2 (OldVal_1) (NewVal_1) 
3 (OldVal_3) (OldVal_3) 
3 (OldVal_21) (OldVal_21) 
3 (OldVal_1) (NewVal_1) 

가 구분 행 재결합 (판정 대체 없었다 곳에는 오리지널 값을 복제)이 얻을 전에 만들어진 테이블과 그 가입 다시과 같이 제약 ID로 그룹화 단일 문자열로 :

1 (OldVal_1) (OldVal_2)  (NewVal_1) (NewVal_2) 
2 (OldVal_2) (OldVal_1)  (NewVal_2) (NewVal_1) 
3 (OldVal_3) (OldVal_21) (OldVal_1) (OldVal_3) (OldVal_21) (NewVal_1) 

그런 다음 시킴으로 빨리 테이블의 데이터를 업데이트 문에서 내에서 그것을 사용합니다. 어쨌든 나는 코드가 약간 정리 될 수 있고 심지어는 단순화 될 수도 있음을 깨닫는다. 정말로 더 큰 문제는이 데이터를 어떻게 저장하고 있는지입니다. 그러나 당신의 상황은 나에게 알려지지 않습니다.

관련 문제