Время прочтения: 4 мин.
В данной публикации рассматривается метод по парсингу информации из xml полей в таблицах различных баз данных в СУБД MS SQL.
В таблицах баз данных встречаются текстовые поля nvarchar(max), которые содержат в себе xml-формат представления данных. Фактически это таблица в таблице со своими столбцами и строками. В качестве примера попробую преобразовать в табличный вид xml строки, пример одной xml строки в таблице ниже:
<description>
<text>Детальная информация:</text>
<grid>
<columns>
<column>
<name>c1</name>
<title>Номер счета</title>
</column>
<column>
<name>c2</name>
<title>ID</title>
</column>
<column>
<name>c3</name>
<title>Номер договора</title>
</column>
<column>
<name>c4</name>
<title>Дата договора</title>
</column>
<column>
<name>c5</name>
<title>Страховой продукт</title>
</column>
<column>
<name>c6</name>
<title>Отсутствующие документы по страховке</title>
</column>
<column>
<name>c7</name>
<title>ФИО сотрудника</title>
</column>
</columns>
<data>
<row>
<c1>**********822</c1>
<c2>921921921921_1</c2>
<c3>123456789_1</c3>
<c4>2022-08-10</c4>
<c5>ДСЖ</c5>
<c6>Заявление на страхование и Поручение владельца счета</c6>
<c7>Фамилия Имя Отчество</c7>
</row>
<row>
<c1>**********823</c1>
<c2>921921921921_2</c2>
<c3>123456789_2</c3>
<c4>2022-08-11</c4>
<c5>ДСЖ</c5>
<c6>Заявление на страхование и Поручение владельца счета</c6>
<c7>Фамилия2 Имя2 Отчество2</c7>
</row>
</data>
</grid>
</description>
Видно, что xml-строки в таблице содержат в себе более 1 строки. Разделение строк в xml идет через <row></row>.
Представим, что данные xml хранятся в таблице xml_data в виде текста в атрибуте xml_type_nv.
Структура таблицы xml_data:
Id (int),
xml_type_nv (nvarchar(max)),
comment (nvarchar(255)
Можно заметить, что в рамках одной строки таблицы имеется id (в нашем случае нумерация), а в поле comment хранится комментарий к одному из xml-файлов.
Благодаря скрипту ниже, реализованному на MS SQL, можно преобразовать xml в табличный вид, что позволяет более оперативно обработать данные и связать их с таблицами в базе данных.
declare @a int; -- вводим переменную a (для цикла)
declare @b int; -- вводим переменную b (для цикла)
declare @docHandle int; -- вводим переменную для функции openxml
declare @xmlDoc nvarchar(max); -- вводим переменную для работы с xml
set @a = 1; -- значение переменной a в цикле
set @b = 1; -- значение переменной b в цикле
while @b >= @a –- когда b будет >= a, тогда работа скрипта закончится.
begin
set @xmlDoc = (select [xml_type_nv] from [SANDBOX].[User].[xml_data] q where q.[id] = @a); --- счетчик, берет из таблицы [xml_data] по 1 xml.
exec sp_xml_preparedocument @docHandle output, @xmlDoc;
with xml_need as (
select *, lag([text]) over (order by id,parentid) www
from openxml(@docHandle, N'/description/grid/data/row') – задаем уровень вложенности в xml, фактический то, что будет идти в таблицу.
),
t_done as (
select
*,
ROW_NUMBER() over (order by xml_s) ro, -- сортировка для учета сдвига от строк
@a as global_ro -- id из таблицы [xml_data] для соединения
from (
select
b.localname,
a.[text],
(select count(*) from (select * from (
select parentid, [text] from xml_need) a
join (select id, localname from (select *, lag([text]) over (order by id,parentid) www
from openxml(@docHandle, N'/description/grid/data/row')) a) b
on a.parentid = b.id where localname = 'c1') eee) as xml_s -- количество строк в xml (по количеству вхождений c1 атрибута)
from (
select parentid, [text] from xml_need) a
join (select id, localname from (select *, lag([text]) over (order by id,parentid) www
from openxml(@docHandle, N'/description/grid/data/row')) a) b
on a.parentid = b.id --- построение связи xml, на этом этапе данные уже почти готовы, однако их требует транспонировать
) v
)
--insert into [SANDBOX].[User].[xml_data_done]
select *
--into [SANDBOX].[User].[xml_data_done]
from (
select
localname,
case
when localname = 'c1'
then [text]
end as 'c1 (Номер счета)',
lead(
case when localname = 'c2'
then [text]
end
, 1) over (order by ro)
as 'c2 (ID)',
lead(
case when localname = 'c3'
then [text]
end
, 2) over (order by ro)
as 'c3 (Номер договора)',
lead(case when localname = 'c4'
then [text]
end, 3) over (order by ro) as 'c4 (Дата договора)',
lead(case when localname = 'c5'
then [text]
end, 4) over (order by ro) as 'c5 (Страховой продукт)',
lead(case when localname = 'c6'
then [text]
end, 5) over (order by ro) as 'c6 (Отсутствующие документы по страховке)',
lead(case when localname = 'c7'
then [text]
end, 6) over (order by ro) as 'c7 (ФИО сотрудника)',
xml_s as 'количество строк',
global_ro as 'id в таблице [xml_data]'
from t_done
where localname <> 'row'
) w
where [c1 (Номер счета)] is not NULL
set @a += 1;
end
Итоговый выход данных в таблице [SANDBOX].[User].[xml_data_done]:
Видно, что все переменные связались верно, далее с таблицей [xml_data] можно связать таблицу [SANDBOX].[User].[xml_data_done] по ключу id и достать комментарии.
При использовании скрипта требуется помнить, что вложенность xml может отличаться, поэтому в следующем коде
from openxml(@docHandle, N'/description/grid/data/row')) a) b
вложенность description/grid/data/row подлежит замене на нужную вложенность.
Также localname может иметь другие имена полей и подлежит замене на необходимые названия полей.