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

99
11

Предположим, что у меня есть 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

Спасибо Майку, я все еще не мог понять, как сопоставить имя и заказ с помощью свойства metx openxml, пожалуйста, предположите, что структура xml в двойном вопросе и в ссылке 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


Переработал script на основе измельчения xml рекурсивно в базу данных


Выполняя ниже script, я получаю ошибку выполнения во время выполнения при преобразовании значения 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


ОК Я выяснил проблему выше script, но все еще не решение. Я думаю, что проблема связана с шаблоном слияния, так как навигация node и Navigations Table имеют не все те же поля, что и в Navigations Table, есть некоторые дополнительные необязательные поля, и, возможно, слияние будет ожидать одинаковое количество полей, а также имена элементов в XML не совсем то же самое в таблице Navigations, которая, по моему мнению, является причиной этой проблемы.


ОБНОВЛЕНИЕ 8-423: 37IS


Проблема осталась такой же даже после синхронизации имен элементов XML (имя, заказ) с именами полей в таблице Navigations, поэтому это может быть связано с неправильным количеством совпадений полей в таблице навигации и элементами под навигацией node в XML


ОБНОВЛЕНИЕ 8-5: 1IS


Похоже, что слияние не выполнимо. coz ParentID ссылается на NavigationId, который является столбцом идентификации, сгенерированным после вставки, поэтому единственный способ, который я предполагаю, - сделать с курсором. Любые предложения отсюда? Ниже приведен последний script после внесения нескольких изменений, это получение родительского идентификатора, сгенерированного открытым 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;

спросил(а) 2021-01-25T17:58:59+03:00 4 месяца, 3 недели назад
1
Решение
99

Я немного запутался в вашей структуре таблицы, но в любом случае вот что-то, что я считаю, делает то, что вы хотите.


Вместо сопоставления с 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 |

ответил(а) 2021-01-25T17:58:59+03:00 4 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема