Вот тестовые данные, которые я использовал в запросах ниже:
declare @T table (XMLCol xml)
insert into @T values
('<computer_book>
<title>Selecting XML Nodes the Fun and Easy Way</title>
<isbn>9999999999999</isbn>
<pages>500</pages>
<backing>paperback</backing>
</computer_book>'),
('<cooking_book>
<title>50 Quick and Easy XML Dishes</title>
<isbn>5555555555555</isbn>
<pages>275</pages>
<backing>paperback</backing>
</cooking_book>')
Вы можете отфильтровать узлы под корневым узлом следующим образом, используя local-name () и список желаемых имен узлов:
select XMLCol.query('/*/*[local-name()=("isbn","pages")]')
from @T
Результат:
<isbn>9999999999999</isbn><pages>500</pages>
<isbn>5555555555555</isbn><pages>275</pages>
Если я правильно вас понимаю, проблема в том, что вы не получите корневой узел.
Этот запрос даст вам пустой корневой узел:
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'/>' as xml)
from @T
Результат:
<computer_book />
<cooking_book />
Из этого я нашел для вас два решения.
Решение 1
Получите узлы из вашей таблицы в табличную переменную, а затем измените XML так, как вам нужно.
-- Table variable to hold the node(s) you want
declare @T2 table (RootNode xml, ChildNodes xml)
-- Fetch the xml from your table
insert into @T2
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'/>' as xml),
XMLCol.query('/*/*[local-name()=("isbn","pages")]')
from @T
-- Add the child nodes to the root node
update @T2 set
RootNode.modify('insert sql:column("ChildNodes") into (/*)[1]')
-- Fetch the modified XML
select RootNode
from @T2
Результат:
RootNode
<computer_book><isbn>9999999999999</isbn><pages>500</pages></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages></cooking_book>
Самое печальное в этом решении то, что оно не работает с SQL Server 2005.
Раствор 2
Получить детали, собрать XML в виде строки и привести его обратно к XML.
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>'+
cast(XMLCol.query('/*/*[local-name()=("isbn","pages")]') as varchar(max))+
'</'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>' as xml)
from @T
Результат:
<computer_book><isbn>9999999999999</isbn><pages>500</pages></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages></cooking_book>
Параметризация узлов
В запросах над узлами, которые вы получаете как дочерние узлы, жестко кодируется в запросе. Вы можете использовать sql:varaible()
, чтобы сделать это вместо этого. Я не нашел способа сделать число узлов динамическим, но вы можете добавить столько, сколько вам нужно, и иметь значение null
для узлов, которые вам не нужны.
declare @N1 varchar(10)
declare @N2 varchar(10)
declare @N3 varchar(10)
declare @N4 varchar(10)
set @N1 = 'isbn'
set @N2 = 'pages'
set @N3 = 'backing'
set @N4 = null
select cast('<'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>'+
cast(XMLCol.query('/*/*[local-name()=(sql:variable("@N1"),
sql:variable("@N2"),
sql:variable("@N3"),
sql:variable("@N4"))]') as varchar(max))+
'</'+XMLCol.value('local-name(/*[1])', 'varchar(100)')+'>' as xml)
from @T
Результат:
<computer_book><isbn>9999999999999</isbn><pages>500</pages><backing>paperback</backing></computer_book>
<cooking_book><isbn>5555555555555</isbn><pages>275</pages><backing>paperback</backing></cooking_book>