コンストラクタを使用する少し異なる方法を次に示します。
DECLARE @sample TABLE
(
[id] int NOT NULL,
[name] varchar(50) NOT NULL
);
INSERT INTO @sample ([id], [name])
SELECT 1, 'one' UNION ALL
SELECT 2, 'two' UNION ALL
SELECT 3, 'three';
SELECT
CONVERT(xml, N'').query
(N'
<doc>
{
element field
{
attribute name {"id"},
text{sql:column("id")}
},
element field
{
attribute name {"name"},
text{sql:column("name")}
}
}
</doc>
')
FROM
@sample
FOR XML PATH(N''), ROOT(N'add');
*編集:これを行う別の方法を考えただけです(ただし、事前に列の知識が必要です)*
繰り返しますが、どちらのアプローチのパフォーマンスへの影響についても確信が持てません。
SELECT
(
SELECT
'id' AS [@name],
[id] AS [data()]
FOR XML PATH('field'), TYPE
) AS [*],
(
SELECT
'name' AS [@name],
[name] AS [data()]
FOR XML PATH('field'), TYPE
) AS [*]
FROM
@sample
FOR XML PATH(N'doc'), ROOT(N'add');
* 更新 2: Aaron Bertrand のコメントに触発された動的だが非効率的な方法 *
これは、アーロンがコメントで参照した投稿で説明されている方法の概念実証でした。(大規模なデータセットではパフォーマンスが大幅に低下します)
-- Inspired by Aaron Bertrand's comment
WITH [cte_KVP]
AS
(
-- Generating Key/Value pairs for columns in a table
-- Courtesey of Mikael Eriksson (http://stackoverflow.com/questions/7341143/flattening-of-a-1-row-table-into-a-key-value-pair-table/)
SELECT
[T2].[N].value(N'local-name(.)', N'sysname') AS [Key],
[T2].[N].value(N'.', N'nvarchar(max)') AS [Value],
[T2].[N].value(N'../GROUP[1]', N'int') AS [GROUP] -- 3. Used for to group the key/value pairs per row
FROM
(
SELECT
*,
ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [GROUP] -- 1. Generating a simple "identity" value.
FROM
@sample
FOR XML PATH(N'Row'), TYPE -- 2. Adding the 'Row' to the path separates each row, and allows us to backtrack via xpath to get the "GROUP" id
) AS [T1]([x])
CROSS APPLY
[T1].[x].nodes(N'Row/*') AS [T2]([N])
WHERE
[T2].[N].value(N'local-name(.)', N'sysname') <> N'GROUP'
)
SELECT
[InnerNodes].[xml] AS [*]
FROM
(
-- Probably preferable to use a table of numbers here
SELECT DISTINCT
[GROUP]
FROM
[cte_KVP]
) AS [Numbers]([Number])
CROSS APPLY
(
-- Generating the xml fragment specified by OP
SELECT
[cte_KVP].[Key] AS [@name],
[cte_KVP].[Value] AS [data()]
FROM
[cte_KVP]
WHERE
[cte_KVP].[GROUP] = [Numbers].[Number]
FOR XML PATH(N'field'), ROOT(N'doc'), TYPE
) AS [InnerNodes]([xml])
FOR XML PATH(N''), ROOT(N'add');