【问题标题】:Insert rows into table in sql server from xml using its nodes and subnodes使用其节点和子节点从 xml 将行插入到 sql server 中的表中
【发布时间】:2014-08-04 13:42:38
【问题描述】:

假设我有一个如下的 XML

<navigations>
    <navigation>
       <name>Home</name>
       <order>1</order>
    </navigation>
    <navigation>
        <name>Sports</name>
        <order>2</order>
        <subnavigations>
            <navigation>
                <name>Basketball</name>
                <order>1</order>
            </navigation>
            <navigation>
                <name>Cricket</name>
                <order>2</order>
            </navigation>
        </subnavigations>
    <navigation/>
</navigations>

SQL 表如下

Navigation(
    NavigationId INT PRIMARY_KEY, 
    Name NVARCHAR(128), Order INT, 
    ParentId INT NULL)

如何将以上 XML 作为输入的记录插入到导航表中?

以下解决方案只能插入所有 Parent 为 NULL 的记录,但如果没有父引用,则无法提供这些记录。有什么想法吗?

CREATE PROCEDURE usp_InsertNavigationsFromXML
                            @xmldoc XML
AS
BEGIN
INSERT INTO Navigations(SequenceOrder, Name)
    SELECT
       Col.value('order[1]', 'int'),  
       Col.value('name[1]', 'nvarchar(100)')
    FROM   @xmldoc.nodes('//navigation') Tab(Col)  END
GO

谢谢 Mike,我仍然不知道如何使用 openxml 元属性映射名称和顺序,请建议,dup question 和 msdn 链接中的 xml 结构略有不同

merge into Navigations as N
using ( 
      select *
      from openxml(@D, '//*') with 
        (
          ID int '@mp:id',
          ParentNavigationId int '@mp:parentid',
          NavVal nvarchar(128) 'text()',
          SequenceOrder int 'WHAT SHOULD BE MAPPED HERE'
        )
      ) as S

更新 8-4-2239IS

基于shredding xml recursively into the database重写了脚本

在执行以下脚本时,我收到运行时错误 Conversion failed when convert the nvarchar value 'Home' to data type int.

现有表中的 XML 似乎存在一些映射问题,您能建议一下吗?

DECLARE @PublicationId INT
SET @PublicationId = 1
DECLARE @xmldoc XML = '<navigations>
    <navigation>
       <name>Home</name>
       <order>1</order>
    </navigation>
    <navigation>
        <name>Sports</name>
        <order>2</order>
        <subnavigations>
            <navigation>
                <name>Basketball</name>
                <order>1</order>
            </navigation>
            <navigation>
                <name>Cricket</name>
                <order>2</order>
            </navigation>
        </subnavigations>
    </navigation>
</navigations>';

-- OpenXML handle
declare @D int;

-- Table that capture outputof merge with mapping between 
-- DOM node id and the identity column elementID in Element 
declare @T table
(
  ID int,
  ParentNavigationId int,
  NavigationId INT
);

-- Parse XML and get a handle
exec sp_xml_preparedocument @D output, @xmldoc;

-- Add rows to Element and fill the mapping table @T
merge into Navigations as N
using (
      select *
      from openxml(@D, '//*') with 
        (
          ID int '@mp:id',
          ParentID int '@mp:parentid',
          NavValue nvarchar(128) 'text()',
          SequenceOrder int 'text()'
        )
      ) as S
on 0 = 1
when not matched by target then
  insert (PublicationId, NavValue,SequenceOrder) values (@PublicationId, S.NavValue, S.SequenceOrder)output S.ID, S.ParentID, inserted.NavigationId into @T;

-- Update parentId in Elemet
update N
set ParentNavigationId =  T2.NavigationId
from Navigations as N
  inner join @T as T1
    on N.NavigationId = T1.NavigationId
  inner join @T as T2
    on T1.ParentNavigationId = T2.ID
-- Relase the XML document
   exec sp_xml_removedocument @D;

更新 8-4:23:16IS

好的,我已经解决了上述脚本的问题,但仍然没有解决方案。我猜的问题是合并模式,因为导航节点和导航表都没有与导航表中相同的字段,还有一些额外的非强制性字段&也许合并会期望相同数量的字段以及 XML 中的元素名称是导航表中的不完全相同,我认为这是导致此问题的原因

更新 8-423:37IS

即使在将 XML 元素名称(名称、顺序)与导航表中的字段名同步后,问题仍然存在,因此可能是由于导航表中的字段数与 XML 中导航节点下的元素不匹配

更新 8-5:1IS

看起来合并不是可行的解决方案,因为 ParentID 被引用到 NavigationId 是插入后生成的标识列,所以我猜唯一的方法是使用光标。这里有什么建议吗?下面是进行一些更改后的最新脚本,这是获取由 open xml 生成的父 ID,但我需要参考 Navigation Id

DECLARE @PublicationId INT
SET @PublicationId = 1
DECLARE @xmldoc XML = '<navigations>
    <navigation>
       <NavValue>Home</NavValue>
       <SequenceOrder>1</SequenceOrder>
    </navigation>
    <navigation>
        <NavValue>Sports</NavValue>
        <SequenceOrder>2</SequenceOrder>
        <subnavigations>
            <navigation>
                <NavValue>Basketball</NavValue>
                <SequenceOrder>1</SequenceOrder>
            </navigation>
            <navigation>
                <NavValue>Cricket</NavValue>
                <SequenceOrder>2</SequenceOrder>
            </navigation>
        </subnavigations>
    </navigation>
</navigations>';

-- OpenXML handle
declare @D int;

-- Table that capture outputof merge with mapping between 
-- DOM node id and the identity column elementID in Element 
declare @T table
(
  ID int,
  ParentNavigationId int,
  NavigationId INT
);

-- Parse XML and get a handle
exec sp_xml_preparedocument @D output, @xmldoc;

-- Add rows to Element and fill the mapping table @T
merge into Navigations as N
using (
      select *
      from openxml(@D, '//navigation') 
      with 
        (
          NavigationId int '@mp:id',
          SequenceOrder int 'SequenceOrder',
          ParentNavigationId int '@mp:parentid',
          NavValue nvarchar(128) 'NavValue'
        )
      ) as S
on 0 = 1
when not matched by target then
  insert (PublicationId, SequenceOrder, ParentNavigationId, NavValue) values (@PublicationId, S.SequenceOrder, S.ParentNavigationId, S.NavValue) 
  output S.NavigationId, S.ParentNavigationId, inserted.NavigationId into @T;

-- Update parentId in Elemet
update N
set ParentNavigationId =  T2.NavigationId
from Navigations as N
  inner join @T as T1
    on N.NavigationId = T1.NavigationId
  inner join @T as T2
    on T1.ParentNavigationId = T2.ID
-- Relase the XML document
   exec sp_xml_removedocument @D;

【问题讨论】:

  • 上面的父引用是什么,你能给我们举个例子说明它现在是如何插入的,你期望什么?
  • @SurendraNathGM 父级应引用导航表中的 NavigationId。如果我使用上面的过程,它会将 NULL 插入到 Parent 中,这是默认的
  • 欺骗问题的 XML 中没有 ParentID。 ParentID 是使用元属性从 XML 中的层次结构生成的。
  • 也许有太多的重写无法为您的 XML 工作。我重新打开问题,让其他人有机会回答。
  • @MikaelEriksson 感谢您的澄清,我仍在努力寻找具有开放 xml 的节点图(名称和顺序),dup question 或 msdn 链接中提供的 xml 节点结构完全不同。下面是我尝试使用 (select * from openxml(@D, '//*') with (ID int '@mp:id', ParentNavigationId int '@mp:parentid', NavVal nvarchar(128) 'text()', SequenceOrder int '???' ) ) as S

标签: sql sql-server-2008 sql-insert


【解决方案1】:

我对你的表结构有点困惑,但无论如何,我相信这是你想要的。

您可以使用合并并输出子 XML 节点并将子节点插入到额外的插入中,而不是像链接答案中那样映射到 ParentID。

如果您需要它在两个以上的级别上工作,则可以在逐级循环的循环中构建合并。

SQL Fiddle

MS SQL Server 2008 架构设置

create table dbo.Navigation
(
  ID int identity primary key,
  ParentID int,
  SequenceOrder int, 
  Name nvarchar(100)
);

查询 1

declare @input xml = '
<navigations>
    <navigation>
       <name>Home</name>
       <order>1</order>
    </navigation>
    <navigation>
        <name>Sports</name>
        <order>2</order>
        <subnavigations>
            <navigation>
                <name>Basketball</name>
                <order>1</order>
            </navigation>
            <navigation>
                <name>Cricket</name>
                <order>2</order>
            </navigation>
        </subnavigations>
    </navigation>
</navigations>';

declare @T table
(
  ID int,
  Navigation xml
);

merge into dbo.Navigation as N
using ( 
      select N.X.value('(name/text())[1]', 'nvarchar(100)') as Name,
             N.X.value('(order/text())[1]', 'int') as SequenceOrder,
             N.X.query('subnavigations/navigation') as Navigation
      from @input.nodes('/navigations/navigation') as N(X)
      ) as S
on 0 = 1
when not matched by target then
  insert (Name, SequenceOrder) values (S.Name, S.SequenceOrder)
output inserted.ID, S.Navigation into @T;

insert into dbo.Navigation(ParentID, Name, SequenceOrder)
select T.ID,
       N.X.value('(name/text())[1]', 'nvarchar(100)'),
       N.X.value('(order/text())[1]', 'int')
from @T as T
  cross apply T.Navigation.nodes('/navigation') as N(X);

select *
from Navigation;

Results

| ID | PARENTID | SEQUENCEORDER |       NAME |
|----|----------|---------------|------------|
|  1 |   (null) |             1 |       Home |
|  2 |   (null) |             2 |     Sports |
|  3 |        2 |             1 | Basketball |
|  4 |        2 |             2 |    Cricket |

【讨论】:

  • 感谢迈克,你拯救了我的一天。你的解决方案就像一个魅力:)
  • 再次感谢,以我的理解范围,我不可能自己实现此代码,感谢您的时间和关注先生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
  • 2014-12-15
  • 1970-01-01
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多