简体   繁体   English

如何在T-SQL中透视XML列的属性

[英]How do I Pivot on an XML column's attributes in T-SQL

I need to perform a pivot on an XML column in a table, where the XML contains multiple elements with a number of attributes. 我需要在表中的XML列上执行一个数据透视表,其中XML包含多个具有许多属性的元素。 The attributes in each element is always the same, however the number of elements will vary. 每个元素中的属性始终相同,但元素的数量会有所不同。 Let me give an example... 让我举个例子...

FormEntryId |               FormXML                                    | DateCreated
====================================================================================
1           |<Root>                                                    | 10/15/2009
            |  <Form>                                                  |
            |    <FormData FieldName="Username" FieldValue="stevem" /> |
            |    <FormData FieldName="FirstName" FieldValue="Steve" /> |
            |    <FormData FieldName="LastName" FieldValue="Mesa" />   |
            |  </Form>                                                 |
            |</Root>                                                   |
            |                                                          |
------------------------------------------------------------------------------------
2           |<Root>                                                    | 10/16/2009
            |  <Form>                                                  |
            |    <FormData FieldName="Username" FieldValue="bobs" />   |
            |    <FormData FieldName="FirstName" FieldValue="Bob" />   |
            |    <FormData FieldName="LastName" FieldValue="Suggs" />  |
            |    <FormData FieldName="NewField" FieldValue="test" />   |
            |  </Form>                                                 |
            |</Root>                                                   |

I need to wind up with a result set for each distinct FieldName attribute values (In this example, Username, FirstName, LastName, and NewField) with their corresponding FieldValue attributes as the value. 我需要结束每个不同的FieldName属性值(在本例中为Username,FirstName,LastName和NewField)的结果集,并将其对应的FieldValue属性作为值。 The results for the example I gave above would look like: 我上面给出的例子的结果如下:

FormEntryId | Username | FirstName | LastName | NewField | DateCreated
======================================================================
1           | stevem   | Steve     | Mesa     | NULL     | 10/15/2009
----------------------------------------------------------------------
2           | bobs     | Bob       | Suggs    | test     | 10/16/2009

I've figured out a way to accomplish this with static columns 我已经找到了使用静态列完成此任务的方法

SELECT
    FormEntryId,
    FormXML.value('/Root[1]/Form[1]/FormData[@FieldName="Username"][1]/@FieldValue','varchar(max)') AS Username,
    FormXML.value('/Root[1]/Form[1]/FormData[@FieldName="FirstName"][1]/@FieldValue','varchar(max)') AS FirstName,
    FormXML.value('/Root[1]/Form[1]/FormData[@FieldName="LastName"][1]/@FieldValue','varchar(max)') AS LastName,
    FormXML.value('/Root[1]/Form[1]/FormData[@FieldName="NewField"][1]/@FieldValue','varchar(max)') AS NewField,
    DateCreated
FROM FormEntry

However I would like to see if there's a method to have the columns be dynamic based on the distinct set of "FieldName" attribute values. 但是,我想看看是否有一种方法可以根据不同的“FieldName”属性值集使列成为动态。

Have a look at this dynamic pivot and more recently this one - you basically need to be able to SELECT DISTINCT FieldName to use this technique to build your query dynamically. 看看这个充满活力的枢纽和最近的这一个 -你基本上需要能够SELECT DISTINCT FieldName使用此技术来动态建立查询。

Here's the full answer for your particular problem (note that there is a column order weakness when generating the list from the distinct attributes in knowing what order the columns should appear): 以下是您的特定问题的完整答案(请注意,在知道列应显示的顺序时,从不同属性生成列表时存在列顺序弱点:

DECLARE @template AS varchar(MAX)
SET @template = 'SELECT 
    FormEntryId
    ,{@col_list}
    ,DateCreated 
FROM FormEntry'

DECLARE @col_template AS varchar(MAX)
SET @col_template = 'FormXML.value(''/Root[1]/Form[1]/FormData[@FieldName="{FieldName}"][1]/@FieldValue'',''varchar(max)'') AS {FieldName}'

DECLARE @col_list AS varchar(MAX)

;WITH FieldNames AS (
    SELECT DISTINCT FieldName
    FROM FormEntry
    CROSS APPLY (
        SELECT X.FieldName.value('@FieldName', 'varchar(255)')
        FROM FormXML.nodes('/Root[1]/Form[1]/FormData') AS X(FieldName)
    ) AS Y (FieldName)
)
SELECT @col_list = COALESCE(@col_list + ',', '') + REPLACE(@col_template, '{FieldName}', FieldName)
FROM FieldNames

DECLARE @sql AS varchar(MAX)
SET @sql = REPLACE(@template, '{@col_list}', @col_list)

EXEC (@sql)

Dynamic pivot isn't built into the language for good reason. 动态数据透视没有内置于语言中。 It would be necessary to scan the entire table containing potential column names before the structure of the result were known. 在结果结构已知之前,有必要扫描包含潜在列名的整个表。 As a result, the table structure of the dynamic pivot statement would be unknown before run time. 因此,动态数据透视表的表结构在运行时之前是未知的。 This creates many problems regarding parsing and interpretation of language. 这在解析和解释语言方面产生了许多问题。

If you decide to implement dynamic pivot on your own, watch out for SQL injection opportunities. 如果您决定自己实施动态数据透视,请注意SQL注入机会。 Be sure to apply QUOTENAME or equivalent to the values you plan to use as column names in your result. 请务必将QUOTENAME或等效值应用于计划用作结果中列名的值。 Also consider what result you want if the number of distinct values in your source that will become column names exceeds the allowed number of columns of a result set. 如果源中将成为列名的不同值的数量超过结果集的允许列数,请考虑所需的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM