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

Posted

技术标签:

【中文标题】将子查询中的多个结果组合成一个逗号分隔值【英文标题】:Combine multiple results in a subquery into a single comma-separated value 【发布时间】:2010-09-11 19:10:47 【问题描述】:

我有两张桌子:

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 是否可行?

【问题讨论】:

【参考方案1】:

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

【讨论】:

这仍然感觉像一个黑客。我仍在使用子查询,所以还有很多额外的处理正在进行。我确信存在更好的解决方案(表重组,或另一种看待问题的方式)。 我不会将其称为 hack。它比游标更有效,而且它没有必要的开销来创建一个临时表,其中的数据按您想要的方式结构化。 耻辱列不能是参数。就目前而言,您需要为每个子关系创建一个函数! 没关系 - 我只需要合并这些特定的列。其余的是“传统”连接。 我不记得没有这种方法的最佳方法。【参考方案2】:

我尝试了 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

【讨论】:

【参考方案3】:

即使这样也能达到目的

样本数据

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

【讨论】:

不知道为什么它没有被采纳,因为它解决了问题而不需要用户功能。您可以在这里看到codecorner.galanter.net/2009/06/25/… 表达的相同想法,该想法早于这个答案,因此可能是“原始” 这里也一样,不知道为什么评价不高 嗨 priyanka,你能告诉我这里是否需要以及为什么需要“and t1.name = t2.name”子句? 这太棒了。我正在寻求优化已接受的答案中列出的 UDF 函数,该答案正在杀死我的服务器。我从 102 秒的搜索时间缩短到不到 1 秒。执行计划比较为 78%-22%,但这与执行时间无关...... 您能解释一下这个查询吗?也许还有指向 MSDN 的链接 FOR XML PATH('') 做什么?我以前从未见过这种情况【参考方案4】:

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

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

逗号应该在前面的末尾并通过%,X,%进行搜索

【讨论】:

【参考方案5】:
SELECT t.ID, 
       t.NAME, 
       (SELECT t1.SOMECOLUMN 
        FROM   TABLEB t1 
        WHERE  t1.F_ID = T.TABLEA.ID) 
FROM   TABLEA t; 

这适用于使用子查询从不同的表中进行选择。

【讨论】:

【参考方案6】:

解决方法如下:

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

使用这个,你也可以改变Joins

【讨论】:

【参考方案7】:

假设您在表 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" >

通过这种方式,您将在客户端执行此操作,但只需一个查询,在数据库和前端之间传递最少的数据

【讨论】:

【参考方案8】:

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

【讨论】:

如果 SQL Server 中有类似的功能,那就完美了。就目前而言,我正在使用 Ben 的解决方案来拼凑我想要的东西。【参考方案9】:

您可能需要提供更多详细信息以获得更准确的回复。

由于您的数据集看起来有点窄,您可能会考虑只对每个结果使用一行并在客户端执行后处理。

因此,如果您真的希望让服务器完成工作,则返回类似

的结果集
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 返回正确构造的结果集,而不是需要进一步处理的东西.您需要更多详细信息吗?我已经简化了表格结构,但我想你已经明白了。【参考方案10】:

我认为您与 COALESCE 的合作是正确的。有关构建逗号分隔字符串的示例,请参见此处:

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

【讨论】:

太棒了!我看过一些讨论 COALESCE 的链接,但它们涉及使用触发器创建 UDF。您提交的链接具有密钥,带有一个 SELECT 语句。我正在添加带有正确解决方案的答案,以便其他人更容易找到。谢谢! 嗨,Ben,我认为答案需要更多细节,即如何创建 UDF 等。一旦我弄清楚了,我会将解决方案添加为社区可编辑的答案。请随时编辑它,之后我会接受它作为答案。很抱歉造成混乱。

以上是关于将子查询中的多个结果组合成一个逗号分隔值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Oracle 中将多行组合成逗号分隔的列表? [复制]

如何在 SQL Server 2005 中将多行组合成以逗号分隔的列表?

用于将子行组合成单行视图的 SQL 查询

将多个mysql查询组合成一个结果

将多个 SQL 查询组合成单个结果

取查询的最后一列,并将结果以逗号分隔在 1 列中