2009-10-16 7 views
6

Ho bisogno di eseguire un pivot su una colonna XML in una tabella, in cui l'XML contiene più elementi con un numero di attributi. Gli attributi in ogni elemento sono sempre gli stessi, tuttavia il numero di elementi varierà. Le faccio un esempio ...Come ruotare su attributi di una colonna XML in 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>             | 

Ho bisogno di finire con un set di risultati per ogni NomeCampo distinti valori di attributo (In questo esempio, nome utente, Nome, Cognome, e NewField) con i corrispondenti fieldValue attributi come il valore. I risultati per l'esempio che ho dato sopra sarebbe simile:

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

ho trovato un modo per ottenere questo risultato con le colonne statiche

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 

Comunque mi piacerebbe vedere se c'è un metodo per le colonne devono essere dinamiche in base al set distinto di valori di attributo "NomeCampo".

+0

+1 per la formattazione del codice piuttosto piacevole. –

risposta

4

Dai uno sguardo allo this dynamic pivot e più recentemente allo this one - devi fondamentalmente essere in grado di utilizzare SELECT DISTINCT FieldName per sviluppare la query in modo dinamico.

Ecco la risposta completa per il vostro problema particolare (si noti che c'è una debolezza ordine delle colonne quando si genera la lista dagli attributi distinti nel sapere che ordine devono apparire le colonne):

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

Wow, questo è molto impressionante! Ho fatto in modo che funzionasse per i miei dati di esempio, ma immagino ci siano un certo numero di prevenzioni e considerazioni di attacco per iniezione su cui devo lavorare, come ha fatto notare Steve. Grazie per l'aiuto! –

+0

Sì, è possibile utilizzare AS QUOTENAME ({FieldName}) per SQL, ma non sono sicuro da dove iniziare a scappare dalla parte XQuery: @FieldName = "{FieldName}". –

1

perno dinamica isn' t costruito nella lingua per una buona ragione. Sarebbe necessario esaminare l'intera tabella contenente potenziali nomi di colonne prima che la struttura del risultato fosse nota. Di conseguenza, la struttura della tabella dell'istruzione pivot dinamica sarebbe sconosciuta prima del runtime. Questo crea molti problemi per quanto riguarda l'analisi e l'interpretazione del linguaggio.

Se si decide di implementare il pivot dinamico da soli, prestare attenzione alle opportunità di iniezione SQL. Assicurati di applicare QUOTENAME o equivalente ai valori che prevedi di utilizzare come nomi di colonne nel risultato. Considerare anche quale risultato si desidera se il numero di valori distinti nella propria origine che diventeranno nomi di colonne supera il numero consentito di colonne di un set di risultati.

Problemi correlati