SQL 服务器 FOR XML 路径使重复节点
SQL Server FOR XML Path make repeating nodes
我想使用 SQL Server 2012 生成以下输出:
<parent>
<item>1</item>
<item>2</item>
<item>3</item>
</parent>
来自同一 table 中的三个不同列(我们称它们为 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>
我做错了什么?
我认为如果您像这样更改列的别名它应该可以工作。这是因为别名相同并且数据的数据类型也可能相同。如果您在 col1、col2 和 col3 中有不同的数据,则不应显示此行为。
SELECT
t.col1 as 'item'
,t.col2 as 'item1'
,t.col3 as 'item2'
FROM tbl t
FOR XML PATH('parent'), TYPE
好的,您不能为此使用路径。相反,使用显式
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')
第二个不需要逆轴,但会重复您的更多查询:
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>
相反,如果您希望逆透视的每一行每行都有一个唯一的父元素,那么,假设每一行都有一些行标识符(我称之为 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>
这里有几个注意事项:如果你使用 FOR XML EXPLICIT,你不能使用 WITH 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
会更快,如果有的话,多个 SELECT
选项很可能被引擎重构为这个)。
添加一个值为 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>
为什么这样做?
没有名称的列作为文本节点插入。在这种情况下,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>
另一种指定不带名称的列的方法是使用通配符 *
作为列别名。
Columns with a Name Specified as a Wildcard Character
在这种情况下没有必要使用通配符,因为具有 NULL 值的列没有列名,但是当您需要实际列中的值但不希望列名出现时,它很有用是一个节点名称。
我想使用 SQL Server 2012 生成以下输出:
<parent>
<item>1</item>
<item>2</item>
<item>3</item>
</parent>
来自同一 table 中的三个不同列(我们称它们为 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>
我做错了什么?
我认为如果您像这样更改列的别名它应该可以工作。这是因为别名相同并且数据的数据类型也可能相同。如果您在 col1、col2 和 col3 中有不同的数据,则不应显示此行为。
SELECT
t.col1 as 'item'
,t.col2 as 'item1'
,t.col3 as 'item2'
FROM tbl t
FOR XML PATH('parent'), TYPE
好的,您不能为此使用路径。相反,使用显式
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')
第二个不需要逆轴,但会重复您的更多查询:
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>
相反,如果您希望逆透视的每一行每行都有一个唯一的父元素,那么,假设每一行都有一些行标识符(我称之为 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>
这里有几个注意事项:如果你使用 FOR XML EXPLICIT,你不能使用 WITH 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
会更快,如果有的话,多个 SELECT
选项很可能被引擎重构为这个)。
添加一个值为 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>
为什么这样做?
没有名称的列作为文本节点插入。在这种情况下,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>
另一种指定不带名称的列的方法是使用通配符 *
作为列别名。
Columns with a Name Specified as a Wildcard Character
在这种情况下没有必要使用通配符,因为具有 NULL 值的列没有列名,但是当您需要实际列中的值但不希望列名出现时,它很有用是一个节点名称。