Вмъкнете редове в таблица в sql сървър от xml, като използвате неговите възли и подвъзли

Да предположим, че имам 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 като вход?

Решението по-долу може да вмъкне само всички записи с родител като 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

Благодаря, Майк, все още не мога да разбера как да съпоставя име и ред с мета свойство openxml, моля, предложете, xml структурата в dup въпроса и в msdn връзката са малко по-различни

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

Преработен е скриптът въз основа на раздробяване на xml рекурсивно в базата данни

Докато изпълнявам скрипта по-долу, получавам грешка по време на изпълнение Преобразуването е неуспешно при преобразуване на стойността на nvarchar „Home“ в тип данни 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, което е колона за идентичност, генерирана след вмъкване, така че единственият начин предполагам е да се направи с курсора. Някакви предложения от тук? По-долу е най-новият скрипт след няколко промени, това е получаване на родителски идентификатор, генериран от отворен xml, но имам нужда от препратка към навигационния идентификатор

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;

person Naga    schedule 04.08.2014    source източник
comment
каква е референцията за родител по-горе, можете ли да ни дадете пример за това как се вмъква сега и какво очаквате?   -  person Surendra    schedule 04.08.2014
comment
Родителят на @SurendraNathGM трябва да препраща към NavigationId в навигационната таблица. Ако използвам горната процедура, тя ще вмъкне NULL в Parent, което е по подразбиране   -  person Naga    schedule 04.08.2014
comment
Няма ParentID в XML във въпроса за подвеждане. ParentID се генерира от йерархиите в XML с помощта на мета свойствата.   -  person Mikael Eriksson    schedule 04.08.2014
comment
Може би има твърде много пренаписване, за да работи за вашия XML. Отварям отново въпроса, за да дам възможност на другите да отговорят.   -  person Mikael Eriksson    schedule 04.08.2014
comment
@MikaelEriksson Благодаря за пояснението, все още се боря да намеря карта на възли (име и ред) с отворен xml, структурата на xml възел, предоставена в dup въпрос или в msdn връзка, е доста различна. По-долу е това, което се опитвам да поправя сливане в навигации като N, използвайки ( select * from openxml(@D, '//*') with ( ID int '@mp:id', ParentNavigationId int '@mp:parentid', NavVal nvarchar(128) 'text()', SequenceOrder int '???' ) ) като S   -  person Naga    schedule 04.08.2014
comment
Не изглежда, че е възможно да се овладее ParentID с помощта на родителската ос, така че предполагам, че openxml не е подходящ. Искате ли това да работи само за две нива или може да бъде по-дълбоко от примерния XML?   -  person Mikael Eriksson    schedule 04.08.2014
comment
@MikaelEriksson, да, така е, хващането на ParentID не е възможно със сливане, предполагам. Само 2 нива трябва да са добре за сега. Имате ли по-добри идеи?   -  person Naga    schedule 04.08.2014
comment
Моля, не редактирайте отговор на въпроса. Ако имате да добавите повече от съществуващия отговор, можете да публикувате свой собствен.   -  person Andrew Barber    schedule 06.08.2014


Отговори (1)


Малко съм объркан относно структурата на вашата таблица, но все пак тук има нещо, което вярвам, че прави това, което искате.

Вместо да картографирате срещу ParentID, както в свързания отговор, можете да използвате обединяване и извеждане на дъщерните XML възли и да вмъкнете дъщерните възли в допълнително вмъкване.

Ако имате нужда това да работи за повече от две нива, възможно е да изградите сливането в цикъл, който върви ниво по ниво.

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;

Резултати:

| ID | PARENTID | SEQUENCEORDER |       NAME |
|----|----------|---------------|------------|
|  1 |   (null) |             1 |       Home |
|  2 |   (null) |             2 |     Sports |
|  3 |        2 |             1 | Basketball |
|  4 |        2 |             2 |    Cricket |
person Mikael Eriksson    schedule 04.08.2014
comment
Благодаря, Майк, спаси ми деня. Вашето решение работи като чар :) - person Naga; 05.08.2014
comment
Благодаря още веднъж, не бих могъл сам да постигна този код с моята степен на разбиране. Благодаря за отделеното време и внимание, сър. - person Naga; 05.08.2014