【发布时间】: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