SQL Server для XML-пути делает повторяющиеся узлы

Я хотел бы создать следующий вывод с помощью SQL Server 2012:

<parent>
  <item>1</item>
  <item>2</item>
  <item>3</item>
</parent>

из трех разных столбцов в одной таблице (мы будем называть их col1, col2 и col3).

Я пытаюсь использовать этот запрос:

SELECT 
  t.col1 as 'item'
 ,t.col2 as 'item'
 ,t.col3 as 'item' 
FROM tbl t 
FOR XML PATH('parent'), TYPE

но я получаю вот что:

<parent>
  <item>123</item>
</parent>

что я здесь делаю не так?

5 ответов


добавьте столбец с NULL в качестве значения для создания отдельного узла элемента для каждого столбца.

SELECT 
  t.col1 as 'item'
 ,NULL
 ,t.col2 as 'item'
 ,NULL
 ,t.col3 as 'item' 
FROM dbo.tbl as t 
FOR XML PATH('parent'), TYPE;

результат:

<parent>
  <item>1</item>
  <item>2</item>
  <item>3</item>
</parent>

SQL Fiddle

почему это работает?

столбцы без имени вставляются как текстовые узлы. В этом случае значение NULL вставляется как текстовый узел между item узлы.

если вы добавите фактические значения вместо NULL, вы увидите, что событие.

SELECT 
  t.col1 as 'item'
 ,'1'
 ,t.col2 as 'item'
 ,'2'
 ,t.col3 as 'item' 
FROM dbo.tbl as t 
FOR XML PATH('parent'), TYPE;

результат:

<parent>
  <item>1</item>1<item>2</item>2<item>3</item></parent>

другой способ указать столбец без имени-использовать подстановочный знак * как псевдоним столбца.

столбцы с именем, указанным как подстановочный знак

в этом случае нет необходимости использовать подстановочный знак, потому что столбцы со значениями NULL не имеют имени столбца, но это полезно, когда вы хотите, чтобы значения из фактических столбцов, но вы не хотите, чтобы имя столбца имя узла.


Ok, вы не можете использовать путь для этого. Вместо этого используйте explicit,

SELECT 1 AS tag,NULL AS parent, t.col1 AS [Parent!1!Item!element],
               t.col2 AS [Parent!1!Item!element],
               t.col3 AS [Parent!1!Item!element]
FROM tbl t
FOR XML EXPLICIT

на самом деле есть несколько способов решить эту проблему с помощью синтаксиса XML-пути.

во-первых, чтобы UNPIVOT ваши результаты сначала, например:

SELECT item as [text()]
FROM 
   (select col1, col2, col3 from tbl) p
UNPIVOT
   (item FOR colHeading IN (col1, col2, col3)) AS unpvt
FOR XML PATH ('item'), ROOT ('parent')

2nd не требует unpivot, но повторяет больше вашего запроса:

select (select col1 as [text()] from tbl for xml path('item'), type)
    ,  (select col2 as [text()] from tbl for xml path('item'), type)
    ,  (select col3 as [text()] from tbl for xml path('item'), type)
for xml path ('parent')

оба они будут объединять несколько строк данных под одним родительским узлом. Например, если у вас есть 2 строки, первая с 1,2,3 и второй 4,5,6, вы получите:

<parent>
    <item>1</item>
    <item>2</item>
    <item>3</item>
    <item>4</item>
    <item>5</item>
    <item>6</item>
</parent>

если вместо этого вы хотите каждая строка, которую вы unpivot, чтобы иметь уникальный родительский элемент в строке, то, предполагая, что у вас есть идентификатор строки в каждой строке( я назову его parentId), вы можете сгруппировать их по этой строке, настроив эти подходы:

SELECT
  (
      SELECT item as [text()]
      FROM 
        (select parentId, col1, col2, col3 from tbl tt where tt.parentid =   t.parentid) p
      UNPIVOT
        (item FOR colHeading IN (col1, col2, col3)) AS unpvt
      FOR XML PATH ('item'), TYPE
  )
FROM tbl t
FOR XML PATH ('parent')

или

select (select col1 as [text()] from tbl tt where t.parentid = tt.parentid for xml path('item'), type)
    ,  (select col2 as [text()] from tbl tt where t.parentid = tt.parentid for xml path('item'), type)
    ,  (select col3 as [text()] from tbl tt where t.parentid = tt.parentid for xml path('item'), type)
from tbl t
for xml path ('parent')

что привело бы к:

<parent>
    <item>1</item>
    <item>2</item>
    <item>3</item>
</parent>
<parent>
    <item>4</item>
    <item>5</item>
    <item>6</item>
</parent>

Sql возиться с демо


пара заметок здесь: Если вы используете для XML EXPLICIT, вы не можете использовать с XMLNAMESPACES (требование, о котором я не упоминал, поэтому я все еще оставляю принятый ответ). А ответ Iheria был также очень полезно, там еще проще возможность я так понял:

SELECT CONVERT(XML, '<item>' + t.col1 
           + '</item><item>' + t.col2 
           + '</item><item>' + t.col3 + '</item>')
FROM tbl t
FOR XML PATH('parent'), TYPE

Я думаю, что это, вероятно, самый простой и наиболее эффективный способ (я не сравнивал его, но я не могу представить себе использование UNPIVOT было бы быстрее и, во всяком случае, несколько скорее всего это во всяком случае, с помощью двигателя).


Я думаю, что если вы измените псевдоним столбцов, как это должно работать. Это связано с тем, что псевдонимы одинаковы и могут быть одинаковым типом данных. Если у вас разные данные в col1, col2 и col3, он не должен показывать это поведение.

SELECT 
t.col1 as 'item'
,t.col2 as 'item1'
,t.col3 as 'item2' 
FROM tbl t 
FOR XML PATH('parent'), TYPE