Suppose if I have an XML as below
<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>
And SQL Table as below
Navigation(
NavigationId INT PRIMARY_KEY,
Name NVARCHAR(128), Order INT,
ParentId INT NULL)
How can I insert records into Navigation Table with above XML as input?
The below solution can only insert all records with Parent as NULL but that cannot be served without parent reference. Any ideas?
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
Thanks Mike, I still could not figure out how to map name and order with openxml meta property, please suggest, the xml structure in dup question and in msdn link are little different
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
UPDATE 8-4-2239IS
Reworked the script based on shredding xml recursively into the database
While executing below script I am getting runtime error Conversion failed when converting the nvarchar value 'Home' to data type int.
Looks like there is some mapping issue with XML from existing Table, can you suggest?
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;
UPDATE 8-4:23:16IS
OK I have figured out the issue with above script, but still not solution. The issue I guess is with merge pattern as both Navigation node and Navigations Table have not all same fields as in Navigations Table there are some extra non-mandatory fields & maybe merge would expect the same number of fields and also the element names in XML is not exactly the same in Navigations Table which I think is the cause of this issue
UPDATE 8-423:37IS
The issue remained same even after syncing the XML element names(name, order) with the fieldnames in Navigations Table, so could be due to mis match number of Fields in Navigations Table vs Elements under Navigation Node in XML
UPDATE 8-5:1IS
Looks like merge is not feasable solution coz ParentID is referenced to NavigationId which is identity column generated after insert, so the only way I guess is to do with cursor. Any suggestions from here? Below is the latest script after making few changes, this is getting parent id generated by open xml but I need reference to 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;