使用带有构面分组和计数的TSQL表到XML

时间:2009-12-08 08:29:02

标签: sql-server xml common-table-expression

我需要将表格转换为具有值计数的XML。

e.g。

Table
Id , Type, AnotherType
1, This, Widget
2, This, Fimble
3, That, Widget

我希望输出像这样。这需要是动态的,因此添加的新列或构面将生成此类输出。

<matrix name="Type">
 <facet name = "this" count=2>
   <id value = 1></id>
   <id value = 2></id>
 </facet>     
 <facet name = "that" count=1>
   <id value = 3></id>
 </facet>
</matrix>
<matrix name="AnotherType">
 <facet name = "Widget" count=2>
   <id value = 1></id>
   <id value = 3></id>
 </facet>
 <facet name = "Fimble" count=1>
   <id value = 2></id>
 </facet>
</matrix>

这可能吗?

如果列/值发生变化,我想要一个处理它的解决方案 - 例如,它会生成一个新的矩阵和依赖于数据的事实。

3 个答案:

答案 0 :(得分:2)

您可以执行此操作,但需要应用UNPIVOT命令和动态SQL。将此分为几个步骤会更好:

1)您的表可以包含各种列。首先应该是获取列的列表。所有列都列在sys.columns表中。以下代码将获取列列表并将其另存为逗号分隔列表。但是,第一列是分开存储的(因为有进一步的目的)。

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)您希望使用UNPIVOTdocumentation here)命令使结构更“程序员友好”。我们需要列的列表来做到这一点。可以使用动态SQL(example here)完成。以下命令执行此任务:

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

在您的示例中,它将生成如下结果:

id | Matrixes    | Facets
-------------------------
2  | AnotherType | Fimble
1  | AnotherType | Widget
3  | AnotherType | Widget
3  | Type        | That
1  | Type        | This
2  | Type        | This

3)最后一件事是实际查询。使用FOR XML AUTO命令就足够了。但是,您应该进行一些嵌套查询以使结构看起来像您想要的那样:

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

和存储过程。我留下了所有的PRINT和SELECT,看看它是如何工作的(如果没有描述的话)。

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'
不过,我猜你在XML结构中犯了一点错误。不应该是这样的(facet标签应该在ID之后关闭吗?):

<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>

答案 1 :(得分:0)

如果您有SQL Server 2005或更高版本,则可以执行this之类的操作。

答案 2 :(得分:0)

请看一下:CTE and Hierarchical XML Result。您可以忽略XSLT部分并坚持使用XML。

编辑:似乎有点诡计。请看这里:SQL XML output to populate a treeview因为它使用纯TSQL CTE指令来生成分层XML。