numeros ejemplo concatenar sql sql-server string-concatenation

ejemplo - ¿Cómo crear una función de SQL Server para "unir" múltiples filas de una subconsulta en un único campo delimitado?



concatenar numeros en sql (13)

Con las otras respuestas, la persona que lee la respuesta debe conocer la tabla del vehículo y crear la tabla y los datos del vehículo para probar una solución.

A continuación se muestra un ejemplo que utiliza la tabla "Information_Schema.Columns" de SQL Server. Al usar esta solución, no es necesario crear tablas ni agregar datos. Este ejemplo crea una lista de nombres de columna separados por comas para todas las tablas en la base de datos.

SELECT Table_Name ,STUFF(( SELECT '','' + Column_Name FROM INFORMATION_SCHEMA.Columns Columns WHERE Tables.Table_Name = Columns.Table_Name ORDER BY Column_Name FOR XML PATH ('''')), 1, 1, '''' )Columns FROM INFORMATION_SCHEMA.Columns Tables GROUP BY TABLE_NAME

Para ilustrar, supongamos que tengo dos tablas de la siguiente manera:

VehicleID Name 1 Chuck 2 Larry LocationID VehicleID City 1 1 New York 2 1 Seattle 3 1 Vancouver 4 2 Los Angeles 5 2 Houston

Quiero escribir una consulta para devolver los siguientes resultados:

VehicleID Name Locations 1 Chuck New York, Seattle, Vancouver 2 Larry Los Angeles, Houston

Sé que esto se puede hacer utilizando los cursores del lado del servidor, es decir:

DECLARE @VehicleID int DECLARE @VehicleName varchar(100) DECLARE @LocationCity varchar(100) DECLARE @Locations varchar(4000) DECLARE @Results TABLE ( VehicleID int Name varchar(100) Locations varchar(4000) ) DECLARE VehiclesCursor CURSOR FOR SELECT [VehicleID] , [Name] FROM [Vehicles] OPEN VehiclesCursor FETCH NEXT FROM VehiclesCursor INTO @VehicleID , @VehicleName WHILE @@FETCH_STATUS = 0 BEGIN SET @Locations = '''' DECLARE LocationsCursor CURSOR FOR SELECT [City] FROM [Locations] WHERE [VehicleID] = @VehicleID OPEN LocationsCursor FETCH NEXT FROM LocationsCursor INTO @LocationCity WHILE @@FETCH_STATUS = 0 BEGIN SET @Locations = @Locations + @LocationCity FETCH NEXT FROM LocationsCursor INTO @LocationCity END CLOSE LocationsCursor DEALLOCATE LocationsCursor INSERT INTO @Results (VehicleID, Name, Locations) SELECT @VehicleID, @Name, @Locations END CLOSE VehiclesCursor DEALLOCATE VehiclesCursor SELECT * FROM @Results

Sin embargo, como puede ver, esto requiere una gran cantidad de código. Lo que me gustaría es una función genérica que me permita hacer algo como esto:

SELECT VehicleID , Name , JOIN(SELECT City FROM Locations WHERE VehicleID = Vehicles.VehicleID, '', '') AS Locations FROM Vehicles

es posible? ¿O algo similar?


De lo que puedo ver FOR XML (como se publicó anteriormente) es la única forma de hacerlo si también quieres seleccionar otras columnas (lo que supongo que haría la mayoría) como lo hace OP. El uso de COALESCE(@var... no permite la inclusión de otras columnas.

Actualización: gracias a programmingsolutions.net hay una manera de eliminar la coma "final". Al convertirlo en una coma inicial y usar la función STUFF de MSSQL, puede reemplazar el primer carácter (coma inicial) con una cadena vacía como se muestra a continuación:

stuff( (select '','' + Column from Table inner where inner.Id = outer.Id for xml path('''') ), 1,1,'''') as Values


El siguiente código funcionará para Sql Server 2000/2005/2008

CREATE FUNCTION fnConcatVehicleCities(@VehicleId SMALLINT) RETURNS VARCHAR(1000) AS BEGIN DECLARE @csvCities VARCHAR(1000) SELECT @csvCities = COALESCE(@csvCities + '', '', '''') + COALESCE(City,'''') FROM Vehicles WHERE VehicleId = @VehicleId return @csvCities END -- //Once the User defined function is created then run the below sql SELECT VehicleID , dbo.fnConcatVehicleCities(VehicleId) AS Locations FROM Vehicles GROUP BY VehicleID


En SQL Server 2005+:

SELECT [VehicleID] , [Name] , [Locations] = Isnull( Stuff( ( SELECT N'', '' + [City] FROM [Locations] WHERE VehicleID = a.VehicleID FOR XML PATH(''''),TYPE ).value(''text()[1]'', ''nvarchar(max)'') , 1, 2, N''''), N'''') FROM [Vehicle] a


En una sola consulta SQL, sin usar la cláusula FOR XML.
Una Expresión de tabla común se utiliza para concatenar recursivamente los resultados.

-- rank locations by incrementing lexicographical order WITH RankedLocations AS ( SELECT VehicleID, City, ROW_NUMBER() OVER ( PARTITION BY VehicleID ORDER BY City ) Rank FROM Locations ), -- concatenate locations using a recursive query -- (Common Table Expression) Concatenations AS ( -- for each vehicle, select the first location SELECT VehicleID, CONVERT(nvarchar(MAX), City) Cities, Rank FROM RankedLocations WHERE Rank = 1 -- then incrementally concatenate with the next location -- this will return intermediate concatenations that will be -- filtered out later on UNION ALL SELECT c.VehicleID, (c.Cities + '', '' + l.City) Cities, l.Rank FROM Concatenations c -- this is a recursion! INNER JOIN RankedLocations l ON l.VehicleID = c.VehicleID AND l.Rank = c.Rank + 1 ), -- rank concatenation results by decrementing length -- (rank 1 will always be for the longest concatenation) RankedConcatenations AS ( SELECT VehicleID, Cities, ROW_NUMBER() OVER ( PARTITION BY VehicleID ORDER BY Rank DESC ) Rank FROM Concatenations ) -- main query SELECT v.VehicleID, v.Name, c.Cities FROM Vehicles v INNER JOIN RankedConcatenations c ON c.VehicleID = v.VehicleID AND c.Rank = 1


Encontré una solución creando la siguiente función:

CREATE FUNCTION [dbo].[JoinTexts] ( @delimiter VARCHAR(20) , @whereClause VARCHAR(1) ) RETURNS VARCHAR(MAX) AS BEGIN DECLARE @Texts VARCHAR(MAX) SELECT @Texts = COALESCE(@Texts + @delimiter, '''') + T.Texto FROM SomeTable AS T WHERE T.SomeOtherColumn = @whereClause RETURN @Texts END GO

Uso:

SELECT dbo.JoinTexts('' , '', ''Y'')


La respuesta de Mun no funcionó para mí, así que hice algunos cambios en esa respuesta para que funcione. Espero que esto ayude a alguien. Usando SQL Server 2012:

SELECT [VehicleID] , [Name] , STUFF((SELECT DISTINCT '','' + CONVERT(VARCHAR,City) FROM [Location] WHERE (VehicleID = Vehicle.VehicleID) FOR XML PATH ('''')), 1, 2, '''') AS Locations FROM [Vehicle]


No creo que haya una forma de hacerlo en una consulta, pero puedes jugar trucos como este con una variable temporal:

declare @s varchar(max) set @s = '''' select @s = @s + City + '','' from Locations select @s

Definitivamente es menos código que caminar sobre un cursor, y probablemente sea más eficiente.


Prueba esta consulta

SELECT v.VehicleId, v.Name, ll.LocationList FROM Vehicles v LEFT JOIN (SELECT DISTINCT VehicleId, REPLACE( REPLACE( REPLACE( ( SELECT City as c FROM Locations x WHERE x.VehicleID = l.VehicleID FOR XML PATH('''') ), ''</c><c>'','', '' ), ''<c>'','''' ), ''</c>'', '''' ) AS LocationList FROM Locations l ) ll ON ll.VehicleId = v.VehicleId


Si está ejecutando SQL Server 2005, puede escribir una función agregada CLR personalizada para manejar esto.

Versión C #:

using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Text; using Microsoft.SqlServer.Server; [Serializable] [Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined,MaxByteSize=8000)] public class CSV:IBinarySerialize { private StringBuilder Result; public void Init() { this.Result = new StringBuilder(); } public void Accumulate(SqlString Value) { if (Value.IsNull) return; this.Result.Append(Value.Value).Append(","); } public void Merge(CSV Group) { this.Result.Append(Group.Result); } public SqlString Terminate() { return new SqlString(this.Result.ToString()); } public void Read(System.IO.BinaryReader r) { this.Result = new StringBuilder(r.ReadString()); } public void Write(System.IO.BinaryWriter w) { w.Write(this.Result.ToString()); } }


Si está utilizando SQL Server 2005, puede usar el comando FOR XML PATH.

SELECT [VehicleID] , [Name] , (STUFF((SELECT CAST('', '' + [City] AS VARCHAR(MAX)) FROM [Location] WHERE (VehicleID = Vehicle.VehicleID) FOR XML PATH ('''')), 1, 2, '''')) AS Locations FROM [Vehicle]

Es mucho más fácil que usar un cursor, y parece funcionar bastante bien.


Tenga en cuenta que el código de Matt dará como resultado una coma adicional al final de la cadena; usar COALESCE (o ISNULL para ese asunto) como se muestra en el enlace en la publicación de Lance utiliza un método similar pero no te deja con una coma extra para eliminar. En aras de la exhaustividad, aquí está el código relevante del enlace de Lance en sqlteam.com:

DECLARE @EmployeeList varchar(100) SELECT @EmployeeList = COALESCE(@EmployeeList + '', '', '''') + CAST(EmpUniqueID AS varchar(5)) FROM SalesCallsEmployees WHERE SalCal_UniqueID = 1


NOTA DE VERSIÓN: Debe utilizar SQL Server 2005 o superior con Nivel de compatibilidad establecido en 90 o superior para esta solución.

Consulte este artículo de MSDN para obtener el primer ejemplo de creación de una función de agregado definida por el usuario que concatena un conjunto de valores de cadena tomados de una columna en una tabla.

Mi humilde recomendación sería dejar de lado la coma adjunta para que pueda usar su propio delimitador ad-hoc, en su caso.

En referencia a la versión de C # del Ejemplo 1:

change: this.intermediateResult.Append(value.Value).Append('',''); to: this.intermediateResult.Append(value.Value);

Y

change: output = this.intermediateResult.ToString(0, this.intermediateResult.Length - 1); to: output = this.intermediateResult.ToString();

De esta forma, cuando use su agregado personalizado, puede optar por usar su propio delimitador, o ninguno, como por ejemplo:

SELECT dbo.CONCATENATE(column1 + ''|'') from table1

NOTA: Tenga cuidado con la cantidad de datos que intenta procesar en su agregado. Si intenta concatenar miles de filas o muchos tipos de datos muy grandes, puede obtener un error de .NET Framework indicando "[t] e el buffer es insuficiente".