2011-10-13 13 views
6

Io uso spesso domande come:Concatenazione di righe con FOR XML, ma con più colonne?

SELECT * 
FROM ThisTable 
     OUTER APPLY (SELECT (SELECT SomeField + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField) A 

spesso mi voglio ottenere più campi concatenati concatenati da questa tabella, invece di uno solo. Ho potuto logicamente fare questo:

SELECT * 
FROM ThisTable 
     OUTER APPLY (SELECT (SELECT SomeField + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField) A 
     OUTER APPLY (SELECT (SELECT SomeField2 + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField2) B 
     OUTER APPLY (SELECT (SELECT SomeField3 + ' ' AS [data()] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR XML PATH ('')) AS ConcatenatedSomeField3) C 

ma sembra scadente e soggetto a errori quando qualcosa deve essere aggiornato; anche SomeTable è spesso una lunga lista di tabelle unite, quindi potrebbe avere anche implicazioni sul rendimento ottenendo ripetutamente le stesse tabelle.

C'è un modo migliore per farlo?

Grazie.

risposta

8

Si potrebbe fare qualcosa di simile. Invece di inviare immediatamente il valore XML a una stringa, questa query utilizza la parola chiave TYPE per restituire un oggetto di tipo xml che può quindi essere interrogato. Le tre funzioni di query eseguono la ricerca nell'oggetto xml per tutte le istanze dell'elemento Somefield e restituiscono un nuovo oggetto xml contenente solo quei valori. Allora la funzione valore estrae i tag XML che circondano i valori e li passa in un varchar (max)

SELECT ThisTable.ID 
     ,[A].query('/Somefield').value('/', 'varchar(max)') AS [SomeField_Combined] 
     ,[A].query('/Somefield2').value('/', 'varchar(max)') AS [SomeField2_Combined] 
     ,[A].query('/Somefield3').value('/', 'varchar(max)') AS [SomeField3_Combined] 
FROM ThisTable 
     OUTER APPLY (
        SELECT (
          SELECT SomeField + ' ' AS [SomeField] 
            ,SomeField2 + ' ' AS [SomeField2] 
            ,SomeField3 + ' ' AS [SomeField3] 
          FROM SomeTable 
          WHERE SomeTable.ID = ThisTable.ID 
          FOR 
          XML PATH('') 
           ,TYPE 
          ) AS [A] 
        ) [A] 
+0

Bella idea ma le prestazioni non sono buone http://dba.stackexchange.com/q/125771/3690 –

3

È possibile creare un CLR User-Defined Aggregate Function che esegue la concatenazione per conto dell'utente.

Il tuo codice sarebbe quindi simile a questo invece.

select S.ID, 
     dbo.Concat(S.SomeField1), 
     dbo.Concat(S.SomeField2), 
     dbo.Concat(S.SomeField3) 
from SomeTable as S 
group by S.ID 
2

Questa è la stessa risposta diedi qui: https://dba.stackexchange.com/questions/125771/multiple-column-concatenation/

Il PO di tale questione fa riferimento la risposta data qui. Potete vedere sotto che a volte la risposta più semplice può essere la migliore. Se SomeTable è costituito da più tabelle, andrei avanti e lo inserirò in un CTE per evitare di avere lo stesso codice complesso più volte.

Ho eseguito alcuni test utilizzando poco più di 6 mil. Con un indice sulla colonna ID.

Ecco cosa mi è venuto in mente.

vostra query iniziale:

SELECT * FROM (
    SELECT t.id, 
      stuff([M].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1], 
      stuff([M].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2] 
    FROM dbo.test t 
    OUTER APPLY(SELECT (
        SELECT id, ','+name AS name 
        ,','+car AS car 
        FROM test WHERE test.id=t.id 
        FOR XML PATH('') ,type) 
       AS M) 
      M) S 
GROUP BY id, SomeField_Combined1, SomeField_Combined2 

Questo ha funzionato per ~ 23 minuti.

Ho eseguito questa versione che è la versione che ho appreso per la prima volta. In un certo senso sembra che dovrebbe richiedere più tempo, ma non è così.

SELECT test.id, 
    STUFF((SELECT ', ' + name 
      FROM test ThisTable 
      WHERE test.id = ThisTable.id 
      FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField, 
    STUFF((SELECT ', ' + car 
      FROM test ThisTable 
      WHERE test.id = ThisTable.id 
      FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField2 
FROM test 
GROUP BY id 

Questa versione ha funzionato in poco più di 2 minuti.