SQL Server XQuery - выбор подмножества - PullRequest
3 голосов
/ 08 августа 2011

Возьмем, к примеру, следующий XML:

Исходные данные

<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>

У меня что-то похожее водин столбец с типом xml базы данных SQL Server 2008.Используя SQL Server XQuery, можно ли получить такие результаты:

Результирующие данные

<computer_book>
    <title>Selecting XML Nodes the Fun and Easy Way</title>
    <pages>500</pages>
</computer_book>

и:

<cooking_book>
    <title>50 Quick and Easy XML Dishes</title>
    <isbn>5555555555555</isbn>
</cooking_book>

Обратите внимание, что я не имею в виду выбор обоих примеров в одном запросе;скорее я выбираю каждый через его первичный ключ (который находится в другом столбце).В каждом случае я пытаюсь выбрать корень и произвольное подмножество детей.Корни могут быть разными, как показано выше, поэтому я не верю, что могу жестко закодировать имя корневого узла в предложение «for xml».

У меня такое чувство, что возможности SQL Server XQuery этого не позволяти это нормально, если это так.Однако если я смогу это сделать, я буду очень признателен за пример.

1 Ответ

3 голосов
/ 09 августа 2011

Вот тестовые данные, которые я использовал в запросах ниже:

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>
...