UPDATE: I\'ve discovered there is a Microsoft Connect item raised for this issue here
When using FOR XML PATH
It would be really nice if FOR XML PATH actually worked more cleanly. Reworking your original example with @table variables:
declare @t1 table (c1 int, c2 varchar(50));
declare @t2 table (c1 int, c2 int, c3 varchar(50));
insert @t1 values
(1, 'Mouse'),
(2, 'Chicken'),
(3, 'Snake');
insert @t2 values
(1, 1, 'Front Right'),
(2, 1, 'Front Left'),
(3, 1, 'Back Right'),
(4, 1, 'Back Left'),
(5, 2, 'Right'),
(6, 2, 'Left');
;with xmlnamespaces( default 'uri:animal')
select a.c2 as "@species",
(
select l.c3 as "text()"
from @t2 l
where l.c2 = a.c1
for xml path('leg'), type
) as "legs"
from @t1 a
for xml path('animal'), root('zoo');
Yields the problem XML with repeated namespace declarations:
Front Right
Front Left
Back Right
Back Left
Right
Left
You can migrate elements between namespaces using XQuery with wildcard namespace matching (that is, *:elementName), as below, but it can be quite cumbersome for complex XML:
;with xmlnamespaces( default 'http://tempuri.org/this/namespace/is/meaningless' )
select (
select a.c2 as "@species",
(
select l.c3 as "text()"
from @t2 l
where l.c2 = a.c1
for xml path('leg'), type
) as "legs"
from @t1 a
for xml path('animal'), root('zoo'), type
).query('declare default element namespace "uri:animal";
{ for $a in *:zoo/*:animal return
{attribute species {$a/@species}}
{ for $l in $a/*:legs return
{ for $m in $l/*:leg return
{ $m/text() }
}
}
} ');
Which yields your desired result:
Front Right
Front Left
Back Right
Back Left
Right
Left