2011-10-13 26 views
6

que a menudo utilizan consultas como:Concatenación de filas con FOR XML, pero con varias columnas?

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

menudo me quiere obtener múltiples campos concatenados concatenados de esta tabla, en lugar de sólo uno. Podría lógicamente hacer esto:

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 

Pero parece horrible y propenso a errores cuando algo necesita ser actualizado; también SomeTable es a menudo una larga lista de tablas unidas, por lo que también podría tener implicaciones de rendimiento al obtener las mismas tablas una y otra vez.

¿Hay una mejor manera de hacerlo?

Gracias.

Respuesta

8

Podría hacer algo como esto. En lugar de enviar inmediatamente el valor XML a una cadena, esta consulta usa la palabra clave TYPE para devolver un objeto tipo xml que luego puede ser consultado. Las tres funciones de consulta buscan el objeto xml para todas las instancias del elemento Somefield y devuelven un nuevo objeto xml que contiene solo esos valores. A continuación, la función de valor despoja a cabo las etiquetas XML que rodean los valores y los pasa a 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

Buena idea, pero el rendimiento no es bueno http://dba.stackexchange.com/q/125771/3690 –

3

Puede crear un CLR User-Defined Aggregate Function que haga la concatenación por usted.

Su código se vería así en su lugar.

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

Ésta es la misma respuesta que he dado aquí: https://dba.stackexchange.com/questions/125771/multiple-column-concatenation/

La OP de esa pregunta hace referencia el respuesta dada aquí. A continuación puede ver que a veces la respuesta más simple puede ser la mejor. Si SomeTable tiene varias tablas, continuaré y lo colocaré en un CTE para evitar tener el mismo código complejo varias veces.

Realicé algunas pruebas con un poco más de 6 mil filas. Con un índice en la columna ID.

Esto es lo que se me ocurrió.

su consulta inicial:

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 

Ésta corrió durante ~ 23 minutos.

Ejecuté esta versión que es la versión que aprendí por primera vez. En cierto modo, parece que debería llevar más tiempo, pero no es así.

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 

Esta versión se ejecutó en poco más de 2 minutos.

Cuestiones relacionadas