2009-10-16 39 views
6

Tôi cần thực hiện một trục xoay trên cột XML trong một bảng, trong đó XML chứa nhiều phần tử với một số thuộc tính. Các thuộc tính trong mỗi phần tử luôn giống nhau, tuy nhiên số lượng phần tử sẽ thay đổi. Hãy để tôi đưa ra một ví dụ ...Làm cách nào để Xoay vòng thuộc tính của cột XML trong T-SQL

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

tôi cần phải đối mặt với vấn kết quả phù hợp với từng FieldName biệt giá trị thuộc tính (Trong ví dụ này, Username, FirstName, LastName, và Newfield) với fieldValue tương ứng của họ thuộc tính như giá trị. Kết quả ví dụ như tôi đã ở trên sẽ giống như:

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

tôi đã tìm ra một cách để thực hiện điều này với các cột tĩnh

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 

Tuy nhiên tôi muốn nhìn thấy nếu có một phương pháp để có các cột động dựa trên các giá trị thuộc tính "FieldName" riêng biệt.

+0

+1 để tạo mã đẹp. –

Trả lời

4

Hãy xem this dynamic pivot và gần đây hơn this one - về cơ bản bạn cần có thể SELECT DISTINCT FieldName để sử dụng kỹ thuật này để tạo truy vấn của bạn một cách linh hoạt.

Dưới đây là câu trả lời đầy đủ cho vấn đề cụ thể của bạn (lưu ý rằng có một điểm yếu để cột khi tạo danh sách từ các thuộc tính khác biệt trong biết những gì đặt các cột sẽ xuất hiện):

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

Ồ, điều này rất ấn tượng! Tôi đã nhận được điều này để làm việc cho dữ liệu mẫu của tôi, nhưng tôi tưởng tượng có một số phòng ngừa tấn công tiêm phòng và cân nhắc mà tôi cần phải làm việc trên, như Steve chỉ ra. Cảm ơn sự giúp đỡ của bạn! –

+0

Có, bạn có thể sử dụng AS QUOTENAME ({FieldName}) cho SQL, nhưng tôi không chắc nơi bắt đầu thoát ra khỏi phần XQuery: @FieldName = "{FieldName}". –

1

động trục isn' t được xây dựng vào ngôn ngữ vì lý do chính đáng. Nó sẽ là cần thiết để quét toàn bộ bảng có chứa tên cột tiềm năng trước khi cấu trúc của kết quả được biết đến. Do đó, cấu trúc bảng của câu lệnh pivot động sẽ không được biết trước thời gian chạy. Điều này tạo ra nhiều vấn đề liên quan đến phân tích và giải thích ngôn ngữ.

Nếu bạn quyết định triển khai trục động của riêng mình, hãy xem các cơ hội chèn SQL. Đảm bảo áp dụng QUOTENAME hoặc tương đương với các giá trị bạn định sử dụng làm tên cột trong kết quả của mình. Ngoài ra, hãy xem xét kết quả bạn muốn nếu số lượng giá trị khác biệt trong nguồn của bạn sẽ trở thành tên cột vượt quá số lượng cột được phép của tập hợp kết quả.

Các vấn đề liên quan