2009-10-16 34 views
6

我需要在表中的XML列上執行數據透視表,其中XML包含具有多個屬性的多個元素。每個元素中的屬性總是相同的,但是元素的數量會有所不同。讓我舉一個例子......如何在T-SQL中轉換XML列的屬性

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

我需要風與每個不同的字段名的結果集的屬性值(在這個例子中,用戶名,名字,姓氏,和新野)及其相應的fieldValue方法在屬性價值。對於例如上述結果我給會是什麼樣子:

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

,我想出了一個辦法靜態列

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 

來完成這項不過我想看看是否有一種方法根據不同的「FieldName」屬性值設置這些列是動態的。

+0

+1相當不錯的代碼格式化。 – 2009-10-16 19:52:55

回答

4

看看this dynamic pivot和最近的this one - 你基本上需要能夠用SELECT DISTINCT FieldName來動態地建立你的查詢。

這裏是爲您的特定問題的全部答案(注意,產生從知道不同的屬性什麼順序列應出現在列表中,當一列命令的弱點):

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) 
+0

哇,這是非常令人印象深刻的!我得到了這個樣本數據,但我想像有一些注入攻擊預防和考慮,我需要工作,如史蒂夫指出的那樣。感謝您的幫助! – 2009-10-16 21:59:16

+0

是的,你可以對SQL使用AS QUOTENAME({FieldName}),但是我不知道從哪裏開始逃離XQuery部分:@FieldName =「{FieldName}」。 – 2009-10-17 02:51:20

1

動態支點ISN」有很好的理由建立在語言之上。在結果結構已知之前,需要掃描包含潛在列名稱的整個表格。因此,動態透視語句的表結構在運行時間之前是未知的。這在解析和解釋語言時產生了許多問題。

如果您決定自行實施動態數據透視表,請留意SQL注入機會。請務必將QUOTENAME或同等效果應用於計劃用作結果中列名稱的值。如果源中將成爲列名的不同值的數量超過結果集的允許列數,請考慮您想要的結果。