插入节点作为父节点

时间:2011-02-23 15:57:17

标签: sql-server xquery

我有XML列,内容如下

<Root>
  <Element>
    <ToMove/>
  </Element>
  <Element>
    <Envelope>
      <Good>SomeValue</Good>
    </Envelope>
  </Element>
 <Element>
    <ToMove/>
 </Element>
 <Element>
    <Envelope>
      <Good>SomeValue</Good>
    </Envelope>
  </Element>
</Root>

我想在Element和ToMove之间添加新节点Envelope。 (Element / Envelope / ToMove)使用XQuery。

我尝试将Envelope / ToMove作为兄弟添加到ToMove但插入不支持添加多个节点。单独添加Envelope然后在下一个语句中添加ToMove似乎不可能,因为已经存在不应该获取ToMove节点的Envelope节点。

有什么想法吗?

编辑:元素节点的顺序和数量是可变的。

2 个答案:

答案 0 :(得分:1)

这可能适合你。代码中的注释描述了我的工作。

-- Setup test data two records with four elements in each
declare @Records table(ID int, Content xml)
insert into @Records values
(1, '<Root>
       <Element>
         <ToMove/>
       </Element>
       <Element>
         <Envelope>
           <Good>SomeValue 1</Good>
         </Envelope>
       </Element>
      <Element>
         <ToMove/>
      </Element>
      <Element>
         <Envelope>
           <Good>SomeValue 2</Good>
         </Envelope>
       </Element>
     </Root>'),
(2, '<Root>
       <Element>
         <ToMove/>
       </Element>
       <Element>
         <Envelope>
           <Good>SomeValue 3</Good>
         </Envelope>
       </Element>
      <Element>
         <ToMove/>
      </Element>
      <Element>
         <Envelope>
           <Good>SomeValue 4</Good>
         </Envelope>
       </Element>
     </Root>')

-- Split the elements, one row each to @T
declare @T table (ID int, Element xml)

insert into @T
select
  ID,
  r.query('.')
from @Records
  cross apply Content.nodes('Root/Element') as r(r)

-- Insert Envelop/ToMove where there exist a ToMove
update @T
set Element.modify('insert <Envelope><ToMove/></Envelope> into (Element[1])')
where Element.exist('Element/ToMove') = 1

-- Remove ToMove from Element node
update @T
set Element.modify('delete Element/ToMove')

-- Save changes back to @Records, recombine elements
update @Records
set Content = 
  (
    select (select T.Element) 
    from @T as T
    where T.ID = R.ID
    for xml path('Root')
  )
from @Records as R

--Result
select *
from @Records

答案 1 :(得分:1)

我最初的问题是我正在向多个目标节点添加新节点。我设法使用两个while循环完成此任务。首先,我遍历包含Element/ToMove节点的所有记录,然后在每个记录中,我遍历Element/ToMove节点的所有实例并添加为兄弟节点Envelope/ToMove。最后一步,我删除了Element/ToMove的所有实例。

我最终得到了以下代码:

-- Create table that will temporarily hold matching records
CREATE TABLE #UpdatedRecords
(
    ExistingId int,
    NewContent xml,
    IsProcessed bit
)

INSERT INTO #UpdatedRecords
SELECT      Id,
            Content,
            0 -- At the beginning, records are not processed
FROM        Records
WHERE       Content.exist( '//Element/ToMove' ) = 1

DECLARE @HasMore bit
DECLARE @Id int
DECLARE @Content xml
DECLARE @Position int   

SET     @HasMore = 1
WHILE   ( @HasMore = 1 )
    BEGIN
        -- Select next unprocessed record
        SELECT  @Id = ExistingId, @Content = NewContent
        FROM    #UpdatedRecords
        WHERE   IsProcessed = 0

        IF @Id IS NULL
            SET @HasMore = 0
        ELSE
            BEGIN
                -- Prepare new Content
                SET     @Position = 1
                WHILE   ( @Content.exist('(//Element/ToMove)[sql:variable("@Position")]') = 1 )
                    BEGIN
                        SET @Content.modify
                        ('
                            insert <Envelope><ToMove /></Envelope> after ((//Element/ToMove)[sql:variable("@Position")])[1]
                         ') 

                        SET @Position = @Position + 1   
                    END     

                -- Update Content and mark record as processed
                UPDATE  #UpdatedRecords
                SET     NewContent = @Content,
                        IsProcessed = 1
                WHERE   ExistingId = @Id
            END

        -- Reset Id
        SET @Id = NULL
    END

Update  #UpdatedRecords 
SET     NewContent.modify('delete //Element/ToMove')

-- Update original records
UPDATE  Records
SET     Contents = NewContents
FROM    #UpdatedRecords
WHERE   Id = ExistingId