如何短语T-SQL水平连接?

时间:2012-05-21 23:26:40

标签: sql-server tsql sql-server-2008-r2

我有一个数据库,其中包括两个表,Labs和LabUsers 如何将LabUsers中的列加入Labs中的单元格。

具体来说,我希望将实验室的用户名连接起来并用子弹(• is Alt+0149)分隔,并让这些用户名按字母顺序从左到右排序。

这是一个例子。

Labs表如下所示:

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL

并且LabUsers看起来像这样:

LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2

我想在Labs表格中得到这个结果:

LabID  LabName     LabUsers
-----  ----------  ---------------------
1      North       Diana•Julia•Romeo
2      North East  Diana•Paul•Paula•Rose
3      South West  NULL

以下是创建表格的脚本:

USE [tempdb];
GO
CREATE TABLE [dbo].[LabUsers]
(
    [LabUserID] [int] PRIMARY KEY CLUSTERED,
    [LabUserName] [nvarchar](50) NOT NULL,
    [LabID] [int] NOT NULL
);
GO
INSERT [dbo].[LabUsers] SELECT 1, N'Diana', 1;
INSERT [dbo].[LabUsers] SELECT 2, N'Paul',  2;
INSERT [dbo].[LabUsers] SELECT 3, N'Paula', 2;
INSERT [dbo].[LabUsers] SELECT 4, N'Romeo', 1;
INSERT [dbo].[LabUsers] SELECT 5, N'Julia', 1;
INSERT [dbo].[LabUsers] SELECT 6, N'Rose',  2;
INSERT [dbo].[LabUsers] SELECT 7, N'Diana', 2;

CREATE TABLE [dbo].[Labs]
(
    [LabID] [int] PRIMARY KEY CLUSTERED,
    [LabName] [nvarchar](50) NOT NULL,
    [LabUsers] [nvarchar](max) NULL
);
GO
INSERT [dbo].[Labs] SELECT 1, N'North',      NULL;
INSERT [dbo].[Labs] SELECT 2, N'North East', NULL;
INSERT [dbo].[Labs] SELECT 3, N'South West', NULL;

3 个答案:

答案 0 :(得分:2)

SELECT l.LabID, l.LabName, LabUsers = STUFF((SELECT N'•' + lu.LabUserName
  FROM dbo.LabUsers AS lu 
  WHERE lu.LabID = l.LabID
  ORDER BY lu.LabUserName
  FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, '')
FROM dbo.Labs AS l;

我认为没有理由将它存储在表中,因为在运行查询时,总是可以在运行时生成代码。如果将它存储在表中,则每次更改表中的任何行时都必须更新它。

但是,如果我不能说服你不要这样做(存储像这样的冗余数据真的很糟糕),你可以尝试这种方式:

;WITH x AS
(
    SELECT l.LabID, l.LabName, x = STUFF((SELECT N'•' + lu.LabUserName
      FROM dbo.LabUsers AS lu 
      WHERE lu.LabID = l.LabID
      ORDER BY lu.LabUserName
      FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, '')
    FROM dbo.Labs AS l
)
UPDATE l
SET LabUsers = x.x
FROM dbo.Labs AS l
INNER JOIN x ON l.LabID = x.LabID;

至于性能测试,我将上述版本与此变体进行比较:

SELECT l.LabID, l.LabName, LabUsers = STUFF((SELECT N'•' + lu.LabUserName
  FROM dbo.LabUsers AS lu 
  WHERE lu.LabID = l.LabID
  ORDER BY lu.LabUserName
  FOR XML PATH('')), 1, 1, '')
FROM dbo.Labs AS l;

在我的系统上,我看到这个答案顶部的初始版本要贵得多。还要注意,将这些方法填充到用户定义的函数中会使它更接近@RThomas提出的串联方法。

答案 1 :(得分:2)

试一试

SELECT  LabName ,
        STUFF(( SELECT  ',' + LabUsers.LabUserName
                FROM    dbo.LabUsers
                WHERE   LabUsers.LabID = Labs.LabID
                ORDER BY LabName
              FOR
                XML PATH('')
              ), 1, 1, '') AS Labusers
FROM    dbo.Labs
ORDER BY LabName

FOR XML PATH('')将您的字符串连接成一个XML结果,STUFF在第一个字符处放置一个“无”字符,例如擦掉不需要的第一个逗号。

答案 2 :(得分:0)

另一种方法是设置一个UDF,将所有实验室用户作为单个字符串返回,如下所示:

CREATE FUNCTION LabUserString
(
@pLabId Int
)
RETURNS NVarChar(Max)
AS
BEGIN

  Declare @pResult NVarChar(Max)

  SELECT @pResult = COALESCE(@pResult + N'•', '') + [LabUserName] 
  FROM LabUsers WHERE LabId = @pLabId

  Return @pResult

END

然后是这样的查询:

Select LabID, LabName, dbo.LabUserString(LabID) AS LabUsers FROM Labs