将子查询中的多个结果合并为一个逗号分隔的值


84

我有两个桌子:

TableA
------
ID,
Name

TableB
------
ID,
SomeColumn,
TableA_ID (FK for TableA)

这种关系是TableA一对多的TableB

现在,我想看到这样的结果:

ID     Name      SomeColumn

1.     ABC       X, Y, Z (these are three different rows)
2.     MNO       R, S

这将不起作用(在子查询中有多个结果):

SELECT ID,
       Name, 
       (SELECT SomeColumn FROM TableB WHERE F_ID=TableA.ID)
FROM TableA

如果我在客户端进行处理,这将是一个小问题。但这意味着我将必须在每个页面上运行X查询,其中X是的结果数TableA

请注意,我不能简单地执行GROUP BY或类似的操作,因为它将为的行返回多个结果TableA

我不确定使用COALESCE或类似方法的UDF是否可以工作?

Answers:


134

即使这样也可以达到目的

样本数据

declare @t table(id int, name varchar(20),somecolumn varchar(MAX))
insert into @t
    select 1,'ABC','X' union all
    select 1,'ABC','Y' union all
    select 1,'ABC','Z' union all
    select 2,'MNO','R' union all
    select 2,'MNO','S'

查询:

SELECT ID,Name,
    STUFF((SELECT ',' + CAST(T2.SomeColumn AS VARCHAR(MAX))
     FROM @T T2 WHERE T1.id = T2.id AND T1.name = T2.name
     FOR XML PATH('')),1,1,'') SOMECOLUMN
FROM @T T1
GROUP BY id,Name

输出:

ID  Name    SomeColumn
1   ABC     X,Y,Z
2   MNO     R,S

13
无法确定为什么未解决此问题,因为它无需用户功能即可解决问题。您可以在此处看到表达相同的想法codecorner.galanter.net/2009/06/25/…,此想法早于此答案,因此可能是“原创”
Paul D'Ambra 2010年

1
同样在这里,不确定为什么这个等级没有更高
Marcel

1
嗨,priyanka,您能告诉我在这里是否和为什么需要“ and t1.name = t2.name”子句吗?
科恩

2
太好了 我一直在寻找优化UDF函数的方法,如所接受的答案中列出的那样,这使服务器瘫痪了。我从102秒的搜索下降到不到1秒。执行计划的比较是78%-22%,但这与执行时间
无关

只是提醒您,您将需要前导',',否则最终将在输出中使用尖括号。
蒂姆·斯卡伯勒

45

1.创建UDF:

CREATE FUNCTION CombineValues
(
    @FK_ID INT -- The foreign key from TableA which is used 
               -- to fetch corresponding records
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @SomeColumnList VARCHAR(8000);

SELECT @SomeColumnList =
    COALESCE(@SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20)) 
FROM TableB C
WHERE C.FK_ID = @FK_ID;

RETURN 
(
    SELECT @SomeColumnList
)
END

2.在子查询中使用:

SELECT ID, Name, dbo.CombineValues(FK_ID) FROM TableA

3.如果使用的是存储过程,则可以执行以下操作:

CREATE PROCEDURE GetCombinedValues
 @FK_ID int
As
BEGIN
DECLARE @SomeColumnList VARCHAR(800)
SELECT @SomeColumnList =
    COALESCE(@SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20)) 
FROM TableB
WHERE FK_ID = @FK_ID 

Select *, @SomeColumnList as SelectedIds
    FROM 
        TableA
    WHERE 
        FK_ID = @FK_ID 
END

1
这仍然感觉像是黑客。我仍在使用子查询,因此,仍在进行大量额外的处理。我确信存在更好的解决方案(表重组或其他解决问题的方法)。
Donnie Thomas

1
我不会称其为hack。它比游标更有效,并且缺少创建临时表的必要开销,该临时表具有按所需方式构造的数据。
Scott Lawrence

1
羞耻的列不能是参数。就目前而言,您需要为每个孩子关系建立一个功能!
John Paul Jones

1
没关系-我只需要合并这些特定的列。其余为“传统”联接。
Donnie Thomas 2009年

我不记得没有此方法的最佳方法。
aF。

11

我认为您在COALESCE方面步入正轨。有关构建逗号分隔的字符串的示例,请参见此处:

http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string


2
太棒了!我看到一些讨论COALESCE的链接,但它们涉及使用触发器创建UDF。您提交的链接具有密钥,带有单个SELECT语句。我正在添加具有正确解决方案的答案,以便其他人更容易找到它。谢谢!
Donnie Thomas

1
嗨,Ben,我认为答案需要更多细节,即如何创建UDF等。一旦弄清了这一点,就将解决方案添加为社区可编辑答案。请随时对其进行编辑,之后我将接受它作为答案。对困惑感到抱歉。
Donnie Thomas

11

在MySQL中,有一个group_concat函数将返回您要的内容。

SELECT TableA.ID, TableA.Name, group_concat(TableB.SomeColumn) 
as SomColumnGroup FROM TableA LEFT JOIN TableB ON 
TableB.TableA_ID = TableA.ID

1
如果SQL Server中有类似的功能,那将是完美的。就目前而言,我正在使用Ben的解决方案将我想要的东西融合在一起。
Donnie Thomas

0

您可能需要提供更多详细信息,以进行更精确的响应。

由于您的数据集似乎有点狭窄,因此您可以考虑仅对每个结果使用一行,然后在客户端执行后处理。

因此,如果您真的想让服务器来工作,则返回一个结果集,例如

ID       Name       SomeColumn
1        ABC        X
1        ABC        Y
1        ABC        Z
2        MNO        R
2        MNO        S

哪个当然是ID上的简单INNER JOIN

将结果集返回客户端后,请维护一个名为CurrentName的变量,并在停止将SomeColumn收集到您希望其执行的有用操作中时将其用作触发器。


我想到了这一点,但是不确定这是否是一个优雅的解决方案-我想让SQL Server返回正确构造的结果集,而不是需要进一步处理的结果集。您需要其他详细信息吗?我简化了表的结构,但我想您已经掌握了。
Donnie Thomas

0

假设您在表A上只有WHERE子句,请创建一个存储过程,从而:

SELECT Id, Name From tableA WHERE ...

SELECT tableA.Id AS ParentId, Somecolumn 
FROM tableA INNER JOIN tableB on TableA.Id = TableB.F_Id 
WHERE ...

然后用它填充一个DataSet ds。然后

ds.Relations.Add("foo", ds.Tables[0].Columns("Id"), ds.Tables[1].Columns("ParentId"));

最后,您可以在页面中添加一个中继器,该中继器将每行的逗号放在

 <asp:DataList ID="Subcategories" DataKeyField="ParentCatId" 
DataSource='<%# Container.DataItem.CreateChildView("foo") %>' RepeatColumns="1"
 RepeatDirection="Horizontal" ItemStyle-HorizontalAlign="left" ItemStyle-VerticalAlign="top" 
runat="server" >

这样,您将在客户端执行此操作,但只需执行一次查询,即可在数据库和前端之间传递最少的数据


0

我尝试了提到的priyanka.sarkar解决方案,但并没有按照OP的要求使其正常工作。这是我最终得到的解决方案:

SELECT ID, 
        SUBSTRING((
            SELECT ',' + T2.SomeColumn
            FROM  @T T2 
            WHERE WHERE T1.id = T2.id
            FOR XML PATH('')), 2, 1000000)
    FROM @T T1
GROUP BY ID

-1

解决方案如下:

SELECT GROUP_CONCAT(field_attr_best_weekday_value)as RAVI
FROM content_field_attr_best_weekday LEFT JOIN content_type_attraction
    on content_field_attr_best_weekday.nid = content_type_attraction.nid
GROUP BY content_field_attr_best_weekday.nid

使用此功能,您还可以更改联接


-1
SELECT t.ID, 
       t.NAME, 
       (SELECT t1.SOMECOLUMN 
        FROM   TABLEB t1 
        WHERE  t1.F_ID = T.TABLEA.ID) 
FROM   TABLEA t; 

这将适用于使用子查询从其他表中进行选择。


-1

我已经查看了所有答案。我认为在数据库插入中应该是这样的:

ID     Name      SomeColumn
1.     ABC       ,X,Y Z (these are three different rows)
2.     MNO       ,R,S

逗号应该在末尾,并按如下方式进行搜索 %,X,%

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.