Tem*_*lar 193 sql sql-server string-concatenation
为了说明,假设我有两个表如下:
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
Run Code Online (Sandbox Code Playgroud)
我想写一个查询来返回以下结果:
VehicleID Name Locations
1 Chuck New York, Seattle, Vancouver
2 Larry Los Angeles, Houston
Run Code Online (Sandbox Code Playgroud)
我知道这可以使用服务器端游标完成,即:
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
Run Code Online (Sandbox Code Playgroud)
但是,正如您所看到的,这需要大量代码.我想要的是一个通用函数,可以让我做这样的事情:
SELECT VehicleID
, Name
, JOIN(SELECT City FROM Locations WHERE VehicleID = Vehicles.VehicleID, ', ') AS Locations
FROM Vehicles
Run Code Online (Sandbox Code Playgroud)
这可能吗?或类似的东西?
Mun*_*Mun 253
如果您使用的是SQL Server 2005,则可以使用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]
Run Code Online (Sandbox Code Playgroud)
它比使用游标容易得多,并且似乎运行得相当好.
Mik*_*ell 86
请注意,Matt的代码将在字符串的末尾产生额外的逗号; 使用COALESCE(或ISNULL),如Lance帖子中的链接所示,使用类似的方法,但不会留下额外的逗号删除.为了完整起见,这里是来自sqlteam.com上Lance链接的相关代码:
DECLARE @EmployeeList varchar(100)
SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') +
CAST(EmpUniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
Run Code Online (Sandbox Code Playgroud)
Mat*_*ton 45
我不相信有一种方法可以在一个查询中完成它,但你可以用一个临时变量来玩这样的技巧:
declare @s varchar(max)
set @s = ''
select @s = @s + City + ',' from Locations
select @s
Run Code Online (Sandbox Code Playgroud)
它肯定比走过光标更少的代码,可能更有效.
Joh*_*n B 23
从我所看到的FOR XML
(如前面所述)是唯一的方法,如果你想像OP一样选择其他列(我猜的最多).使用COALESCE(@var...
不允许包含其他列.
更新:感谢programmingsolutions.net,有一种方法可以删除"尾随"逗号.通过使其成为一个领先的逗号并使用STUFF
MSSQL 的功能,您可以用空字符串替换第一个字符(前导逗号),如下所示:
stuff(
(select ',' + Column
from Table
inner where inner.Id = outer.Id
for xml path('')
), 1,1,'') as Values
Run Code Online (Sandbox Code Playgroud)
Zun*_*Tzu 23
在单个SQL查询中,不使用FOR XML子句.
公用表表达式用于递归地连接结果.
-- 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
Run Code Online (Sandbox Code Playgroud)
Ste*_*ong 17
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
Run Code Online (Sandbox Code Playgroud)
您可以使用FOR JSON语法
即
SELECT per.ID,
Emails = JSON_VALUE(
REPLACE(
(SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
,'"},{"_":"',', '),'$[0]._'
)
FROM Person per
Run Code Online (Sandbox Code Playgroud)
结果将成为
Id Emails
1 abc@gmail.com
2 NULL
3 def@gmail.com, xyz@gmail.com
Run Code Online (Sandbox Code Playgroud)
这甚至会使您的数据包含无效的XML字符
''},{" ":"'是安全的,因为如果你的数据包含'"},{" ":"',它将被转义为"},{\"_ \":\"
您可以用任何字符串分隔符替换','
您可以使用新的STRING_AGG功能
Bin*_*ony 14
以下代码适用于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
Run Code Online (Sandbox Code Playgroud)
我通过创建以下函数找到了解决方案:
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
Run Code Online (Sandbox Code Playgroud)
用法:
SELECT dbo.JoinTexts(' , ', 'Y')
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
128633 次 |
最近记录: |