Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve an element without sub-elements in SQL Server

I am using SQL Server 2008 and I want to retrieve XML elements and return the element itself and its attributes without any sub elements and without the text(). Eg the following XML has 4 nodes (doc, apple, b, banana):

<doc>
<apple type="bramley"> tasty <b>yum!</b> </apple>
<banana color="yellow" shape="bendy"> nice in smoothies </banana>
</doc>

I would like to return:

<doc/>
<apple type="bramley"/>
<b/>
<banana color="yellow" shape="bendy"/>

Eg doc should be returned without any sub-nodes and apple should be returned without the b sub-node. But the problem is, if I use the SQL Server nodes and query method I can't remove the sub-nodes. Using SQL Server 2008 the closest I have is:

declare @x xml = '<doc>
  <apple type="bramley"> tasty <b>yum!</b> </apple>
  <banana color="yellow" shape="bendy"> nice in smoothies </banana>
</doc>';

select 
  c.query('local-name(.)') as Node,
  c.query('for $e in . return <xx> {$e/@*} </xx>') as Attr
from @x.nodes('//*') as T(c);

This gets the name of each node (using local-name) and the attributes of the node and returns:

Node    Attr
----    ----
doc     <xx />
apple   <xx type="bramley" />
b       <xx />
banana  <xx color="yellow" shape="bendy" />

I realize I can process this result, convert Attr to varchar, replace xx by the Node column and convert back to XML. But is there an easier way without string manipulation?

PS: If it helps, I don't mind if the solution uses SQL Server 2008 or SQL Server 2012.

like image 970
Ubercoder Avatar asked Dec 16 '12 23:12

Ubercoder


1 Answers

Usually you would use element construction with dynamic tag names, but SQL Server doesn't support this:

declare @x xml = '<doc>
  <apple type="bramley"> tasty <b>yum!</b> </apple>
  <banana color="yellow" shape="bendy"> nice in smoothies </banana>
</doc>';

select 
  c.query('local-name(.)') as Node,
  c.query('for $e in . return element { local-name($e) } { $e/@* } </xx>') as Attr
from @x.nodes('//*') as T(c);

As an XQuery Update alternative (tested with SQL Server 2012), we can fetch all nodes (with all contents) and delete their subnotes.

DECLARE @x xml = '<doc>test
  <apple type="bramley"> tasty <b>yum!</b> </apple>
  <banana color="yellow" shape="bendy"> nice in smoothies </banana>
</doc>';

-- Fetch all nodes
SET @x = @x.query('//*')
-- Delete all subnodes
SET @x.modify('delete /*/node()')

select 
  c.query('.') as NewNode
from @x.nodes('/*') as T(c);
like image 130
Jens Erat Avatar answered Oct 04 '22 22:10

Jens Erat