Вот немного другой способ использования конструкторов.
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: Динамичный, но неэффективный метод, вдохновленный комментариями Аарона Бертрана *
Это было доказательством концепции метода, описанного в посте, на который Аарон ссылался в своих комментариях. (Это ужасно работает на больших наборах данных)
-- 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');