Sql server 如何表达T-SQL水平联接?

Sql server 如何表达T-SQL水平联接?,sql-server,tsql,sql-server-2008-r2,Sql Server,Tsql,Sql Server 2008 R2,我有一个数据库,其中包括两个表,实验室和实验室用户 如何将LabUsers中的列加入Labs中的单元格 具体来说,我希望将实验室的用户名连接起来,并用一个项目符号(•is Alt+0149)分隔,并将这些用户名按字母顺序从左到右排序 这里有一个例子 Labs表如下所示: LabID LabName LabStudents ----- ---------- ----------- 1 North NULL 2 North East NULL 3

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

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

这里有一个例子

Labs
表如下所示:

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL
LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2
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
LabUsers
看起来像这样:

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL
LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2
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
我想在
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;
我认为绝对没有理由将其存储在表中,因为在运行查询时,始终可以在运行时生成代码。如果将其存储在表中,则每次更改表中的任何行时都必须对其进行更新

但是,如果我无法说服您不要这样做(像这样存储冗余数据确实不好),您可以尝试以下方法:

;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

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

但是,如果我无法说服您不要这样做(像这样存储冗余数据确实不好),您可以尝试以下方法:

;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。

试试看

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路径(“”)将字符串连接到一个XML结果中,填充在第一个字符处添加一个“nothing”字符,例如,删除不需要的第一个逗号。

尝试一下

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路径(“”)将字符串连接到一个XML结果中,然后在第一个字符处添加一个“nothing”字符,例如,删除不需要的第一个逗号。

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

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL
LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2
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
然后是这样的查询:

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL
LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2
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

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

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL
LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2
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
然后是这样的查询:

LabID  LabName     LabStudents
-----  ----------  -----------
1      North       NULL
2      North East  NULL
3      South West  NULL
LabUserID   LabUserName   LabID
---------   -----------   -----    
1           Diana         1
2           Paul          2
3           Paula         2
4           Romeo         1
5           Julia         1
6           Rose          2
7           Diana         2
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

你为什么不能在代码中这么做?Abe Miessler-我知道。但由于冲突,我希望将其作为存储过程。有什么原因不能在代码中这样做吗?Abe Miessler-我有。但由于冲突,我希望将其作为存储过程。这实际上是一个常见问题的常见模式。这个FOR XML PATH子查询的工作原理类似于MySQL中的GROUP_CONCAT-对于LabID的每个值,它都会构建一个由找到的每个lab用户组成的字符串。奇怪的是,与您的答案相比,我的答案表现如何。。。我已经多次使用合并方法,但像您这样的MVP似乎总是喜欢xml路径方法。@r好吧,您可以随时比较它们。:-)我怀疑每行调用的UDF也会增加一些开销只要我尝试访问你头脑中的RAM,而不是我可用的磁盘I/O。“但是我很好奇,所以我会的。”谢谢你的确认。同样,在进入非常大的数据集之前,您强调的差异可能在很大程度上是不相关的,尤其是对于一次性操作。这实际上是一个常见问题的常见模式。这个FOR XML PATH子查询的工作原理类似于MySQL中的GROUP_CONCAT-对于LabID的每个值,它都会构建一个由找到的每个lab用户组成的字符串。奇怪的是,与您的答案相比,我的答案表现如何。。。我已经多次使用合并方法,但像您这样的MVP似乎总是喜欢xml路径方法。@r好吧,您可以随时比较它们。:-)我怀疑每行调用的UDF也会增加一些开销只要我尝试访问你头脑中的RAM,而不是我可用的磁盘I/O。“但是我很好奇,所以我会的。”谢谢你的确认。再说一次,在进入非常大的数据集之前,您强调的差异可能在很大程度上是无关紧要的,尤其是在一次性操作中。我将把答案留给后代,但我自己的测试表明,公认的答案表现得更好。事实上有一点。我会把答案留给后代,但我自己的测试表明,被接受的答案表现得更好。事实上有一点。你也是+1,因为它与被接受的方法大致相同。你也是+1,因为它与被接受的方法大致相同。