如何在SQL Server中将多行连接成一列?

时间:2009-08-09 02:55:29

标签: sql sql-server tsql

我已经搜索了这个问题的答案,但我无法弄明白。我对SQL Server比较陌生,但还没有很好的语法。我有这个数据结构(简化):

Table "Users"         | Table "Tags":
UserID    UserName    | TagID    UserID    PhotoID
1         Bob         | 1        1         1
2         Bill        | 2        2         1
3         Jane        | 3        3         1
4         Sam         | 4        2         2
-----------------------------------------------------
Table "Photos":              | Table "Albums":
PhotoID   UserID    AlbumID  | AlbumID     UserID
1         1         1        | 1           1
2         1         1        | 2           3
3         1         1        | 3           2
4         3         2        |
5         3         2        |

我正在寻找一种方法来获取所有照片信息(简单)以及该照片的所有标记,例如CONCAT(username, ', ') AS Tags,最后删除了最后一个逗号。我有一段时间试图这样做。我在this article中尝试了这个方法,但是当我尝试运行查询说我不能使用DECLARE语句时出现错误...你们有什么想法吗?做了什么?我正在使用VS08和其中安装的数据库(我通常使用MySQL,所以我不知道这是什么类型的DB真的是......它是一个.mdf文件?)

3 个答案:

答案 0 :(得分:13)

好的,我觉得我需要加入评论How do you concat multiple rows into one column in SQL Server?并提供更优选的答案。

我很抱歉,但使用这样的标量值函数会导致性能下降。只需打开SQL事件探查器,看一下使用调用表的标量函数时会发生什么。

此外,不鼓励使用“更新变量”技术进行连接,因为该功能在将来的版本中可能不会继续。

执行字符串连接以使用FOR XML PATH的首选方法。

select
 stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
 ,*
from photos
order by photoid;

有关FOR XML PATH如何工作的示例,请考虑以下内容,假设您有一个包含两个名为“id”和“name”的字段的表

SELECT id, name
FROM table
order by name
FOR XML PATH('item'),root('itemlist')
;

给出:

<itemlist><item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item></itemlist>

但如果你忽略ROOT,你会得到一些不同的东西:

SELECT id, name
FROM table
order by name
FOR XML PATH('item')
;

<item><id>2</id><name>Aardvark</a></item><item><id>1</id><name>Zebra</name></item>

如果你输入一个空的PATH字符串,你就更接近普通的字符串连接:

SELECT id, name
FROM table
order by name
FOR XML PATH('')
;

<id>2</id><name>Aardvark</a><id>1</id><name>Zebra</name>

现在来了一个非常棘手的问题...如果你命名一个以@符号开头的列,它就会变成一个属性,如果一个列没有名字(或你称之为[*]),那么它也遗漏了那个标签:

SELECT ',' + name
FROM table
order by name
FOR XML PATH('')
;

,Aardvark,Zebra

现在最后,为了去掉前导逗号,STUFF命令进来.STUFF(s,x,n,s2)从位置x开始拉出s的n个字符。取而代之的是s2。所以:

SELECT STUFF('abcde',2,3,'123456');

给出:

a123456e

现在看看我上面的标记列表查询。

select
 stuff((select ', ' + t.tag from tags t where t.photoid = p.photoid order by tag for xml path('')),1,2,'') as taglist
 ,*
from photos
order by photoid;

对于每张照片,我都有一个子查询,它抓取标签并将它们(按顺序)与一个commma和一个空格连接起来。然后我在一个stuff命令中包围该子查询以去除前导逗号和空格。

我为任何拼写错误道歉 - 我实际上并没有在自己的机器上创建表来测试它。

罗布

答案 1 :(得分:3)

我要创建一个UDF:

create function GetTags(PhotoID int) returns @tags varchar(max)
as
begin
    declare @mytags varchar(max)
    set @mytags = ''

    select @mytags = @mytags + ', ' + tag from tags where photoid = @photoid

    return substring(@mytags, 3, 8000)
end

然后,您所要做的就是:

select GetTags(photoID) as tagList from photos

答案 2 :(得分:0)

Street_Name   ; Street_Code

  1. west | 14
  2. 东部 | 7
  3. 西部+东部| 714

如果要显示两个不同的行concat本身,怎么办? (我的意思是我想从选择结果中显示最后一行。我的表有第一条记录和第二条记录)