¿Cómo usar COALESCE con varias filas y sin coma anterior?

27

Estoy tratando de lograr lo siguiente:

California | Los Angeles, San Francisco, Sacramento
Florida    | Jacksonville, Miami

Desafortunadamente, estoy obteniendo ", Los Ángeles, San Francisco, Sacramento, Jacksonville, Miami"

Puedo lograr los resultados deseados con la función STUFF, pero me preguntaba si hay una forma más limpia de hacerlo con COALESCE.

STATE       | CITY
California  | San Francisco
California  | Los Angeles
California  | Sacramento
Florida     | Miami
Florida     | Jacksonville 


DECLARE @col NVARCHAR(MAX);
SELECT @col= COALESCE(@col, '') + ',' + city
FROM tbl where city = 'California';
SELECT @col;

Gracias

usuario2732180
fuente

Respuestas:

45

Este podría ser el enfoque más limpio que buscas. Básicamente, verifique si la variable ya se ha inicializado. Si no es así, configúrelo en la cadena vacía y agregue la primera ciudad (sin comas iniciales). Si es así, entonces agregue una coma, luego agregue la ciudad.

DECLARE @col nvarchar(MAX);
SELECT @col = COALESCE(@col + ',', '') + city
  FROM dbo.tbl WHERE state = 'California';

Por supuesto, eso solo funciona para llenar una variable por estado. Si está sacando la lista para cada estado, uno a la vez, hay una mejor solución de una vez:

SELECT [state], cities = STUFF((
    SELECT N', ' + city FROM dbo.tbl
    WHERE [state] = x.[state]
    FOR XML PATH(''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 2, N'')
FROM dbo.tbl AS x
GROUP BY [state]
ORDER BY [state];

Resultados:

state       cities
----------  --------------------------------------
California  San Francisco, Los Angeles, Sacramento  
Florida     Miami, Jacksonville

Para ordenar por nombre de ciudad dentro de cada estado:

SELECT [state], cities = STUFF((
    SELECT N', ' + city FROM dbo.tbl
    WHERE [state] = x.[state]
    ORDER BY city
    FOR XML PATH(''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 2, N'')
FROM dbo.tbl AS x
GROUP BY [state]
ORDER BY [state];

En Azure SQL Database o SQL Server 2017+, puede usar la nueva STRING_AGG()función :

SELECT [state], cities = STRING_AGG(city, N', ')
  FROM dbo.tbl
  GROUP BY [state]
  ORDER BY [state];

Y ordenado por nombre de ciudad:

SELECT [state], cities = STRING_AGG(city, N', ') 
                         WITHIN GROUP (ORDER BY city)
  FROM dbo.tbl
  GROUP BY [state]
  ORDER BY [state];
Aaron Bertrand
fuente
Gracias Aaron Mi solución actual es casi idéntica a la suya, excepto que estoy usando DISTINCT en lugar de GROUP BY.
user2732180
2
@ user2732180 Debe usar GROUP BY, ya que es más probable que realice la concatenación una vez por estado. Con DISTINCT aplicará la misma concatenación para cada instancia de California, por ejemplo, y solo entonces descartará todo el trabajo que generó esos duplicados.
Aaron Bertrand
6

Solo para agregar a la respuesta de Aaron anterior ...

Tenga en cuenta que ORDER BYpuede romperse al incluir solo el último elemento en su consulta. En mi caso, no estaba agrupando, así que no estoy seguro si eso hace la diferencia. Estoy usando SQL 2014. En mi caso, tengo algo como value1, value2, value3 ... pero mi resultado en la variable fue solo value3.


Aaron comentó que decir:

Esto se ha informado al menos cuatro veces en Connect:

  1. En resultados de concatenación variable y orden por filtros (como la condición where)
  2. (n) la construcción de varchar de ResultSet falla cuando se agrega ORDER BY
  3. Asignar una variable local de un SELECT ordenado con APLICACIONES CRUZADAS y una función con valores de tabla solo devuelve el último valor
  4. Al concatenar valores varchar (max) / nvarchar (max) de una variable de tabla, se pueden devolver resultados incorrectos si se filtra y ordena por una columna de clave no primaria

Ejemplo de respuesta de Microsoft:

El comportamiento que estás viendo es por diseño. El uso de operaciones de asignación (concatenación en este ejemplo) en consultas con la cláusula ORDER BY tiene un comportamiento indefinido.

La respuesta también hace referencia a KB 287515:

PRB: el plan de ejecución y los resultados de las consultas de concatenación agregadas dependen de la ubicación de la expresión

La solución es usar FOR XML PATH(el segundo enfoque en la respuesta de Aaron) si el orden de concatenación es importante y, por supuesto, si desea asegurarse de incluir todos los valores. Ver también:

nvarchar concatenación / index / nvarchar (max) comportamiento inexplicable en Stack Overflow

ebol2000
fuente