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,因为它与被接受的方法大致相同。