2012-04-30 13 views
11

He escrito esta consulta con la ayuda de google para crear una lista delimitada de una tabla pero no entendí nada de esta consulta.¿Qué hace esta consulta para crear una lista delimitada por comas de SQL Server?

Puede alguien explicar lo que está sucediendo

SELECT 
    E1.deptno, 
    allemp = Replace ((SELECT E2.ename AS 'data()' 
         FROM emp AS e2 
         WHERE e1.deptno = e2.DEPTNO 
         FOR xml PATH('')), ' ', ', ') 
FROM EMP AS e1 
GROUP BY DEPTNO; 

me da resultado

10 CLARK, KING, MILLER 
20 SMITH, JONES, SCOTT, ADAMS, FORD 
30 ALLEN, WARD, MARTIN, BLAKE, TURNER, JAMES 
+1

** Tenga en cuenta que el código se fallan por texto que contiene personajes como><** y obtendrá la expansión personaje como '' <, '>' '' & hay una mejor manera de hacer esto concatenación, vea: http://stackoverflow.com/a/5031297/65223 –

Respuesta

35

La forma más sencilla de explicarlo es observar cómo funciona FOR XML PATH para XML real.Imagine una sencilla tabla Employee:

EmployeeID  Name 
1    John Smith 
2    Jane Doe 

Usted podría utilizar

SELECT EmployeeID, Name 
FROM emp.Employee 
FOR XML PATH ('Employee') 

Esto crearía XML de la siguiente manera

<Employee> 
    <EmployeeID>1</EmployeeID> 
    <Name>John Smith</Name> 
</Employee> 
<Employee> 
    <EmployeeID>2</EmployeeID> 
    <Name>Jane Doe</Name> 
</Employee> 

Extracción del 'empleado' de PATH elimina las etiquetas XML exteriores por lo que este consulta:

SELECT Name 
FROM Employee 
FOR XML PATH ('') 

Crearía

<Name>John Smith</Name> 
    <Name>Jane Doe</Name> 

Lo que se está haciendo no es lo ideal, 'datos()' El nombre de columna obliga a un error de SQL porque está tratando de crear una etiqueta XML que no es una etiqueta legal, por lo se genera el siguiente error:

El nombre de columna 'Data()' contiene un identificador XML no válido como lo requiere FOR XML; '(' (0x0028) es el primer carácter en falta

La consulta correlacionada esconde este error y apenas genera el XML sin etiquetas:.

SELECT Name AS [Data()] 
FROM Employee 
FOR XML PATH ('') 

crea

John Smith Jane Doe 

Usted están reemplazando espacios con comas, bastante autoexplicativo ...

Si yo fuera usted, adaptaría la consulta un poco:

SELECT E1.deptno, 
     STUFF((SELECT ', ' + E2.ename 
       FROM emp AS e2 
       WHERE e1.deptno = e2.DEPTNO 
       FOR XML PATH('') 
      ), 1, 2, '') 
FROM EMP AS e1 
GROUP BY DEPTNO; 

Al no tener alias de columna significará sin etiquetas XML son creados, y la adición de la coma dentro de la consulta de selección significa cualquier nombre con espacios en los que no se producirán errores, STUFF eliminará la primera coma y el espacio.

ADENDA

Para más detalles sobre lo KM ha dicho en un comentario, ya que esto parece ser cada vez un poco más de puntos de vista, la forma correcta de escapar caracteres XML sería utilizar .value de la siguiente manera:

SELECT E1.deptno, 
     STUFF((SELECT ', ' + E2.ename 
       FROM emp AS e2 
       WHERE e1.deptno = e2.DEPTNO 
       FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') 
FROM EMP AS e1 
GROUP BY DEPTNO; 
+1

¡Muy buena explicación, gracias! –

+3

+1 buena explicación, pero ** TENGA EN CUENTA QUE ESTE CÓDIGO FALLARÁ PARA EL TEXTO QUE CONTENGA CARACTERES COMO><& ** obtendrá la expansión de caracteres como '<', '>' '&' hay una mejor manera de hacer esta concatenación , mira: http://stackoverflow.com/a/5031297/65223 –

+1

Te falta una sola cita para finalizar '' NVARCHAR (MAX) '' – wilsjd

0

La consulta externa recupera una lista de números de departamento, y la sub consulta se ejecute para cada número de departamento para devolver todos nombres que pertenecen a ese departamento. La subconsulta usa la instrucción FOR XML para formatear el resultado en una lista separada por comas de una sola línea.

6

Desmóntelo paso a paso, desde adentro hacia afuera.

Paso 1:

Ejecutar la consulta interna y ver lo que produce:

SELECT E2.ename AS 'data()' 
FROM emp AS e2 
WHERE e2.DEPTNO = 10 
FOR XML PATH('') 

usted debe conseguir una cosa salida como:

CLARK KING MILLER 

Paso 2:

El REPLACE simplemente reemplaza espacios con , - convirtiendo así su salida en

CLARK, KING, MILLER 

Paso 3:

La consulta externa obtiene el valor deptno - además de los resultados de la consulta interna - y produce su última resultado.

1

SQL Server 2017 hace que esto sea mucho más fácil con el new STRING_AGG. Recientemente encontré esta publicación y cambié mi estrategia STUFF/FOR XML para usar la nueva función de cadena. También evita la necesidad de hacer un JOIN/SUBQUERY adicional y la sobrecarga de FOR XML (y los problemas de codificación impar) y difícil de interpretar SQL.

SELECT E1.deptno, 
     STRING_AGG(E1.ename, ', ') AS allemp 
FROM EMP AS e1 
GROUP BY DEPTNO; 

Nota: También asegúrese de check out the counterpart STRING_SPLIT para hacer el trabajo con datos de SQL delimitados mucho más fácil.

Cuestiones relacionadas