2011-02-23 107 views
2

我有XML列,像这样插入节点作为父

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

我想元素和ToMove之间添加新节点信封的内容。 (元素/信封/ ToMove)使用XQuery。

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

任何想法?

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

+0

正确的XQuery解决方案(身份功能的修改),不能在你的环境中使用:SQL Server不实现一个完整的标准抱怨XQuery引擎用函数声明。 – 2011-02-23 17:35:15

+0

我最初的问题是我正在向多个目标节点添加新节点。 – Enes 2011-02-24 14:29:08

回答

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 
+0

谢谢Mikael,但你的例子不适合我,因为我可以有多个Element节点。 – Enes 2011-02-24 14:28:07

+0

@Enes请更新您的示例x​​ml,以显示您的实际情况。 – 2011-02-24 14:31:06

+0

@Enes我发现你已经解决了你的问题,但仅仅为了它的乐趣,我用新版本更新了我的答案。 – 2011-02-24 18:58:50

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