You can do this but you need to apply UNPIVOT
command and dynamic SQL. It is better when you divide this into steps:
1) Your table can have various columns. What you should to first is to get the list of columns. All the columns are listed in sys.columns
table. The following piece of code will get the list of columns and save it as comma delimited list. However, first column is stored separatly (becuase of the further purpose).
DECLARE @firstColumn NVARCHAR(256)
DECLARE @columnList NVARCHAR(MAX)
SELECT @firstColumn = name
FROM sys.columns
WHERE object_id = OBJECT_ID(@tableName)
AND column_id = 1
SET @columnList = N''
SELECT @columnList = @columnList + name + N','
FROM sys.columns
WHERE object_id = OBJECT_ID(@tableName)
AND column_id > 1
SET @columnList = LEFT(@columnList,LEN(@columnList)-1)
2) You want to use UNPIVOT
(documentation here) command to make the structure more "programmer friendly". We need the list of columns to do that. It can be done using dynamic SQL (example here). The following command does the job:
DECLARE @cmd NVARCHAR(MAX)
SET @cmd = N'INSERT INTO #unPivoted
SELECT id, Matrixes, Facets
FROM
(SELECT ' + @firstColumn + N',' + @columnList + N' FROM ' + @tableName + N') p
UNPIVOT
(Facets FOR Matrixes IN(' + @columnList + N')) AS unpvt
ORDER BY Matrixes,Facets,id'
EXEC sp_executesql @cmd
In your example it will generate result like this:
id | Matrixes | Facets
-------------------------
2 | AnotherType | Fimble
1 | AnotherType | Widget
3 | AnotherType | Widget
3 | Type | That
1 | Type | This
2 | Type | This
3) The last thing is the actual query. It is enough to use FOR XML AUTO
command. You should however make some nested queries to make the structure look like you want:
SELECT matrix.name, facet.name, facet.id_count AS count, id.id AS value
FROM
(SELECT DISTINCT Matrixes AS name FROM #unPivoted) matrix
INNER JOIN
(SELECT Matrixes AS matrix_name, Facets AS name, COUNT(id) AS id_count FROM #unPivoted GROUP BY Matrixes, Facets) facet
ON matrix.name = facet.matrix_name
INNER JOIN
(SELECT Facets AS facet_name, id FROM #unPivoted) id
ON facet.name = id.facet_name
ORDER BY matrix.name, facet.name, id.id
FOR XML AUTO
And the stored procedure. I left all the PRINTs and SELECTs for you to see how does it work (if the description is not enought).
CREATE PROCEDURE usp_tableToXML
@tableName NVARCHAR(256)
AS
BEGIN
SET NOCOUNT ON;
-- 1. Get columns from the table
DECLARE @firstColumn NVARCHAR(256)
DECLARE @columnList NVARCHAR(MAX)
SELECT @firstColumn = name
FROM sys.columns
WHERE object_id = OBJECT_ID(@tableName)
AND column_id = 1
SET @columnList = N''
SELECT @columnList = @columnList + name + N','
FROM sys.columns
WHERE object_id = OBJECT_ID(@tableName)
AND column_id > 1
SET @columnList = LEFT(@columnList,LEN(@columnList)-1)
PRINT @firstColumn
PRINT @columnList
-- 2. Unpivot the table
CREATE TABLE #unPivoted
(
id INT,
Matrixes VARCHAR(30),
Facets VARCHAR(30)
)
DECLARE @cmd NVARCHAR(MAX)
SET @cmd = N'INSERT INTO #unPivoted
SELECT id, Matrixes, Facets
FROM
(SELECT ' + @firstColumn + N',' + @columnList + N' FROM ' + @tableName + N') p
UNPIVOT
(Facets FOR Matrixes IN(' + @columnList + N')) AS unpvt
ORDER BY Matrixes,Facets,id'
PRINT @cmd
EXEC sp_executesql @cmd
SELECT * FROM #unPivoted
-- 3. The query
SELECT matrix.name, facet.name, facet.id_count AS count, id.id AS value
FROM
(SELECT DISTINCT Matrixes AS name FROM #unPivoted) matrix
INNER JOIN
(SELECT Matrixes AS matrix_name, Facets AS name, COUNT(id) AS id_count FROM #unPivoted GROUP BY Matrixes, Facets) facet
ON matrix.name = facet.matrix_name
INNER JOIN
(SELECT Facets AS facet_name, id FROM #unPivoted) id
ON facet.name = id.facet_name
ORDER BY matrix.name, facet.name, id.id
FOR XML AUTO
END
GO
EXEC usp_tableToXML 'Types'
Btw, I guess that you made a little mistake in your XML structure. Shouldn't it be like this (facet
tag should be closed after ids?):
<matrix name="AnotherType">
<facet name="Fimble" count="1">
<id value="2" />
</facet>
<facet name="Widget" count="2">
<id value="1" />
<id value="3" />
</facet>
</matrix>
<matrix name="Type">
<facet name="That" count="1">
<id value="3" />
</facet>
<facet name="This" count="2">
<id value="1" />
<id value="2" />
</facet>
</matrix>