Вставить узел в качестве родителя - PullRequest
2 голосов
/ 23 февраля 2011

У меня есть столбец XML с таким содержимым

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

Я хочу добавить новый узел Envelope между Element и ToMove. (Элемент / Конверт / ToMove) с использованием XQuery.

Я пытался добавить Envelope / ToMove как одноуровневое в ToMove, но insert не поддерживает добавление нескольких узлов. Добавление одного конверта, а затем добавление ToMove в следующем операторе не представляется возможным, поскольку уже есть узлы Envelope, которые не должны получать узел ToMove.

Есть идеи?

РЕДАКТИРОВАТЬ: порядок и количество узлов элемента являются переменными.

Ответы [ 2 ]

1 голос
/ 24 февраля 2011

Моей первоначальной проблемой было добавление нового узла в несколько узлов назначения. Мне удалось выполнить эту задачу, используя два цикла 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 
1 голос
/ 24 февраля 2011

Это может сделать это для вас.Комментарии в коде описывают то, что я делаю.

-- 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
...