【问题标题】: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 是否可行?

【问题讨论】:

    标签: sql-server tsql join


    【解决方案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 等。一旦我弄清楚了,我会将解决方案添加为社区可编辑的答案。请随时编辑它,之后我会接受它作为答案。很抱歉造成混乱。
              猜你喜欢
              • 2010-10-04
              • 2021-07-02
              • 2019-03-17
              • 1970-01-01
              • 2015-04-04
              • 2016-10-27
              • 2014-01-13
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多