如何基于XML参数更新表(How to update a table based on an XML

2019-09-17 15:40发布

我有一个表,我想更新基于在XML参数的值的VARCHAR领域之一。

我有如下表:

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

我想用下面的XML更新Constraint_Value领域:

<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 Management Studio中无需任何设置运行):

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

这使结果:

(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一直保持不变。

如何更新与在XML参数指定的所有元素的领域?

Answer 1:

利用递归CTE的可以让我得到你正在寻找的结果。 如下面所示。 但ATT至少它不是一个光标/ while循环;)

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


Answer 2:

我觉得这里的问题无关的XML。 这是一个UPDATE将只更新一次,无论是如何存在的许多加入行的每一行。 我想你可以添加一个WHERE子句和一个while循环来让所有的换人:

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

只要确保这个遵循设置@@行数的声明。



Answer 3:

我知道这已经回答了,但我很好奇,看看是否有办法做到这一点不使用CTE。 无论如何更大的问题是真的,你是存储2个数据的同一列/一行。 这再加上,你不能一个更新语句内连续两次更新同一行导致您的问题的事实。 反正我的方法是这样(我道歉的时间提前了复杂性):

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

它看起来很多混乱比它并且可以清理,如果你在那里有一些UDF的。 但basicaly我打断你的多值列到多行。 打开这个

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

这个

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

我做的xml文件相同。 把它变成一个关键数据对这样设置

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)

然后使用,在我从声明的一部开拓创新的表来更新数据。 无论如何,我实现了代码可以清理一下,甚至可能简单,但,这是我想出了这个概念。 真正更大的问题是你如何存储这些数据。 但是,你的情况是未知的这样害我储备的判断。



文章来源: How to update a table based on an XML parameter