ОБНОВЛЕНИЕ: я обнаружил, что существует объект Microsoft Connect, повышенный для этой проблемы здесь
При использовании
FOR XML PATH
иWITH XMLNAMESPACES
для объявления пространства имен по умолчанию я копирую объявление пространства имен в любых высокоуровневых узлах для вложенных запросов, которые используют ДЛЯ XML, я споткнулся через несколько решений онлайн, но я не полностью убежден...Вот полный пример
/* drop table t1 drop table t2 */ create table t1 ( c1 int, c2 varchar(50)) create table t2 ( 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')
Каково лучшее решение?
Если я правильно понял, вы имеете в виду поведение, которое вы можете увидеть в запросе, подобном этому:
DECLARE @Order TABLE (
OrderID INT,
OrderDate DATETIME)
DECLARE @OrderDetail TABLE (
OrderID INT,
ItemID VARCHAR(1),
ItemName VARCHAR(50),
Qty INT)
INSERT @Order
VALUES
(1, '2010-01-01'),
(2, '2010-01-02')
INSERT @OrderDetail
VALUES
(1, 'A', 'Drink', 5),
(1, 'B', 'Cup', 2),
(2, 'A', 'Drink', 2),
(2, 'C', 'Straw', 1),
(2, 'D', 'Napkin', 1)
;WITH XMLNAMESPACES('http://test.com/order' AS od)
SELECT
OrderID AS "@OrderID",
(SELECT
ItemID AS "@od:ItemID",
ItemName AS "data()"
FROM @OrderDetail
WHERE OrderID = o.OrderID
FOR XML PATH ('od.Item'), TYPE)
FROM @Order o
FOR XML PATH ('od.Order'), TYPE, ROOT('xml')
Который дает следующие результаты:
<xml xmlns:od="http://test.com/order">
<od.Order OrderID="1">
<od.Item xmlns:od="http://test.com/order" od:ItemID="A">Drink</od.Item>
<od.Item xmlns:od="http://test.com/order" od:ItemID="B">Cup</od.Item>
</od.Order>
<od.Order OrderID="2">
<od.Item xmlns:od="http://test.com/order" od:ItemID="A">Drink</od.Item>
<od.Item xmlns:od="http://test.com/order" od:ItemID="C">Straw</od.Item>
<od.Item xmlns:od="http://test.com/order" od:ItemID="D">Napkin</od.Item>
</od.Order>
</xml>
Как вы сказали, пространство имен повторяется в результатах подзапросов.
Это поведение является особенностью, согласно разговору на devnetnewsgroup (сайт теперь не существует), хотя там есть возможность проголосовать за его изменение.
Предлагаемое мною решение - вернуться к FOR XML EXPLICIT
:
SELECT
1 AS Tag,
NULL AS Parent,
'http://test.com/order' AS [xml!1!xmlns:od],
NULL AS [od:Order!2],
NULL AS [od:Order!2!OrderID],
NULL AS [od:Item!3],
NULL AS [od:Item!3!ItemID]
UNION ALL
SELECT
2 AS Tag,
1 AS Parent,
'http://test.com/order' AS [xml!1!xmlns:od],
NULL AS [od:Order!2],
OrderID AS [od:Order!2!OrderID],
NULL AS [od:Item!3],
NULL [od:Item!3!ItemID]
FROM @Order
UNION ALL
SELECT
3 AS Tag,
2 AS Parent,
'http://test.com/order' AS [xml!1!xmlns:od],
NULL AS [od:Order!2],
o.OrderID AS [od:Order!2!OrderID],
d.ItemName AS [od:Item!3],
d.ItemID AS [od:Item!3!ItemID]
FROM @Order o INNER JOIN @OrderDetail d ON o.OrderID = d.OrderID
ORDER BY [od:Order!2!OrderID], [od:Item!3!ItemID]
FOR XML EXPLICIT
И увидеть следующие результаты:
<xml xmlns:od="http://test.com/order">
<od:Order OrderID="1">
<od:Item ItemID="A">Drink</od:Item>
<od:Item ItemID="B">Cup</od:Item>
</od:Order>
<od:Order OrderID="2">
<od:Item ItemID="A">Drink</od:Item>
<od:Item ItemID="C">Straw</od:Item>
<od:Item ItemID="D">Napkin</od:Item>
</od:Order>
</xml>
Альтернативное решение, которое я видел, заключается в добавлении объявления XMLNAMESPACES
после сборки xml во временную переменную:
declare @xml as xml;
select @xml = (
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'))
;with XmlNamespaces( 'uri:animal' as an)
select @xml for xml path('') , root('zoo');