【问题标题】:Convert multiple rows into one with comma as separator [duplicate]用逗号作为分隔符将多行转换为一行[重复]
【发布时间】:2010-10-27 14:55:11
【问题描述】:

如果我发出SELECT username FROM Users 我会得到这个结果:

用户名 -------- 保罗 约翰 玛丽

但我真正需要的是 一个 行,其中所有值用逗号分隔,如下所示:

保罗、约翰、玛丽

我该怎么做?

【问题讨论】:

    标签: tsql rows csv


    【解决方案1】:
     select
       distinct  
        stuff((
            select ',' + u.username
            from users u
            where u.username = username
            order by u.username
            for xml path('')
        ),1,1,'') as userlist
    from users
    group by username
    

    之前有错别字,上面的工作正常

    【讨论】:

    • 你是个天才。此外,这会在列表前面放置一个空格。将 STUFF 选项设为 1、2,它将删除该空间。我现在需要弄清楚这到底是怎么回事。
    • 免责声明:对于包含<& 等特殊字符的数据,此不起作用
    • @BoratSagdiyev - kehrk 错了,FOR XML 自动编码,请参阅 sqlfiddle.com/#!6/b824a/1
    • @kehrk - 嗯......不。工作正常。 sqlfiddle.com/#!6/b824a/1
    • 应该是公认的答案
    【解决方案2】:

    这应该适合你。一直测试到 SQL 2000。

    create table #user (username varchar(25))
    
    insert into #user (username) values ('Paul')
    insert into #user (username) values ('John')
    insert into #user (username) values ('Mary')
    
    declare @tmp varchar(250)
    SET @tmp = ''
    select @tmp = @tmp + username + ', ' from #user
    
    select SUBSTRING(@tmp, 0, LEN(@tmp))
    

    【讨论】:

    • +1,但select SUBSTRING(@tmp, 0, LEN(@tmp)) 看起来不正确(对我来说),而显然工作(我试过了)。 substring 上的 MSDN page 的...turgid...散文未能阐明它为什么起作用,但我想终点是 start_expression + length_expression 没有更正 start_expression,因为如果你从一个更少的数字开始与1 相比,它以“第一个字符”开头(例如1),我猜它有点靠后门。不过,我想我会改用select SUBSTRING(@tmp, 1, LEN(@tmp) - 1)
    • 是的,显然,因为select SUBSTRING('testing', -2, 5) 给了我们'te'(例如,正是select SUBSTRING('testing', 1, 2) 给我们的),因为在这两种情况下,结果(独占)结束索引都是3。不是我想要依赖的行为。你这样做有什么特别的原因吗?
    • 没有什么特别的原因,只是因为我的根是 C++,所以我习惯了零偏移算术......
    • @BoratSagdiyev -- 你是对的,这正是它正在做的事情。该语法扩展为有效的迷你光标。一般来说,如果您不关心特殊字符的 XML 转义,Hogan 下面提供的 FOR XML PATH 解决方案在大型情况下更快。
    • SQL server 2017/SQL Azure 为此目的支持 STRING_AGG - docs.microsoft.com/en-us/sql/t-sql/functions/… stackoverflow.com/a/42778050/1545569
    【解决方案3】:

    对几种方法的好评:

    http://blogs.msmvps.com/robfarley/2007/04/07/coalesce-is-not-the-answer-to-string-concatentation-in-t-sql/

    文章文案-

    Coalesce 不是 T-SQL 中字符串连接的答案这些年来我看到很多关于使用 COALESCE 函数在 T-SQL 中实现字符串连接的帖子。这是此处的示例之一(借自 Readifarian Marc Ridey)。

    DECLARE @categories varchar(200)
    SET @categories = NULL
    
    SELECT @categories = COALESCE(@categories + ',','') + Name
    FROM Production.ProductCategory
    
    SELECT @categories
    

    这个查询可能相当有效,但需要注意,并且应该正确理解 COALESCE 的使用。 COALESCE 是 ISNULL 的版本,它可以接受两个以上的参数。它返回参数列表中不为空的第一件事。所以实际上它与连接无关,下面的代码完全一样 - 没有使用 COALESCE:

    DECLARE @categories varchar(200)
    SET @categories = ''
    
    SELECT @categories = @categories + ',' + Name
    FROM Production.ProductCategory
    
    SELECT @categories
    

    但是数据库的无序特性使得这不可靠。 T-SQL(还)没有连接函数的全部原因是,这是一个聚合,元素的顺序很重要。使用这种字符串连接的变量赋值方法,您实际上可能会发现返回的答案中没有所有值,特别是如果您希望将子字符串按特定顺序放置。考虑以下内容,当我希望它返回“,Bikes,Clothing,Components,Accessories”时,它在我的机器上只返回“,Accessories”:

    DECLARE @categories varchar(200)
    SET @categories = NULL
    
    SELECT @categories = COALESCE(@categories + ',','') + Name
    FROM Production.ProductCategory
    ORDER BY LEN(Name)
    
    SELECT @categories
    

    更好的是使用一种考虑顺序的方法,该方法已包含在 SQL2005 中,专门用于字符串连接 - FOR XML PATH('')

    SELECT ',' + Name
    FROM Production.ProductCategory
    ORDER BY LEN(Name)
    FOR XML PATH('') 
    

    在我最近比较使用子查询时的 GROUP BY 和 DISTINCT 的帖子中,我演示了 FOR XML PATH('') 的用法。看看这个,你会看到它在子查询中是如何工作的。 'STUFF' 功能仅用于删除前导逗号。

    USE tempdb;
    GO
    CREATE TABLE t1 (id INT, NAME VARCHAR(MAX));
    INSERT t1 values (1,'Jamie');
    INSERT t1 values (1,'Joe');
    INSERT t1 values (1,'John');
    INSERT t1 values (2,'Sai');
    INSERT t1 values (2,'Sam');
    GO
    
    select
        id,
        stuff((
            select ',' + t.[name]
            from t1 t
            where t.id = t1.id
            order by t.[name]
            for xml path('')
        ),1,1,'') as name_csv
    from t1
    group by id
    ; 
    

    FOR XML PATH 是您可以在子查询中使用 ORDER BY 的少数情况之一。另一个是TOP。当您使用未命名的列和 FOR XML PATH('') 时,您将获得直接连接,没有 XML 标记。这确实意味着字符串将是 HTML 编码的,因此,如果您要连接可能具有

    【讨论】:

    • Alex,我添加了整篇文章以防原始链接失效。我希望你能接受这些变化。谢谢。陈奎。
    • @AlexKuznetsov - 仅供参考 - 这篇文章已有 7 年的历史,其中包含自 SQL 2008 发布以来不真实的信息。
    • @Hogan 答案也很老了,而且是在 2008 年根本没有被广泛采用的时候写的。我认为保持这个答案是最新的没有任何价值。
    • @AlexKuznetsov 最后一个帮助我在内联查询中使用。感谢您的详细回答。
    • +1 供您使用 FOR XML('') 版本,我正在努力获取按 id 分区的聚合行。您的示例不仅修复了它,而且我了解它是如何工作的。
    【解决方案4】:

    基于 mwigdahls 的回答。如果你还需要在这里进行分组是如何让它看起来像

    group, csv
    'group1', 'paul, john'
    'group2', 'mary'
    
        --drop table #user
    create table #user (groupName varchar(25), username varchar(25))
    
    insert into #user (groupname, username) values ('apostles', 'Paul')
    insert into #user (groupname, username) values ('apostles', 'John')
    insert into #user (groupname, username) values ('family','Mary')
    
    
    select
        g1.groupname
        , stuff((
            select ', ' + g.username
            from #user g        
            where g.groupName = g1.groupname        
            order by g.username
            for xml path('')
        ),1,2,'') as name_csv
    from #user g1
    group by g1.groupname
    

    【讨论】:

      【解决方案5】:

      您可以使用此查询来完成上述任务:

      DECLARE @test NVARCHAR(max)  
      SELECT @test = COALESCE(@test + ',', '') + field2 FROM #test
      SELECT field2 = @test 
      

      有关详细信息和分步说明,请访问以下链接 http://oops-solution.blogspot.com/2011/11/sql-server-convert-table-column-data.html

      【讨论】:

        【解决方案6】:
        DECLARE @EmployeeList varchar(100)
        
        SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') + 
           CAST(Emp_UniqueID AS varchar(5))
        FROM SalesCallsEmployees
        WHERE SalCal_UniqueID = 1
        
        SELECT @EmployeeList
        

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

        【讨论】:

          【解决方案7】:

          在 SQLite 中,这更简单。我认为 MySQL、MSSql 和 Orable 有类似的实现

          CREATE TABLE Beatles (id integer, name string );
          INSERT INTO Beatles VALUES (1, "Paul");
          INSERT INTO Beatles VALUES (2, "John");
          INSERT INTO Beatles VALUES (3, "Ringo");
          INSERT INTO Beatles VALUES (4, "George");
          SELECT GROUP_CONCAT(name, ',') FROM Beatles;
          

          【讨论】:

            【解决方案8】:

            您可以使用 stuff() 将行转换为逗号分隔值

            select
            EmployeeID,
            stuff((
              SELECT ',' + FPProjectMaster.GroupName 
                  FROM     FPProjectInfo AS t INNER JOIN
                          FPProjectMaster ON t.ProjectID = FPProjectMaster.ProjectID
                  WHERE  (t.EmployeeID = FPProjectInfo.EmployeeID)
                          And t.STatusID = 1
                          ORDER BY t.ProjectID
                   for xml path('')
                   ),1,1,'') as name_csv
            from FPProjectInfo
            group by EmployeeID;
            

            感谢@AlexKuznetsov 提供参考以获得此答案。

            【讨论】:

              【解决方案9】:

              MS SQL Server 2005/2008 中一个简洁灵活的解决方案是创建 CLR Agregate 函数。

              您会在google 上找到很多文章(附代码)。

              看起来this article 使用 C# 引导您完成整个过程。

              【讨论】:

                【解决方案10】:

                如果你是通过 PHP 来执行的,那么这个呢?

                $hQuery = mysql_query("SELECT * FROM users");
                while($hRow = mysql_fetch_array($hQuery)) {
                    $hOut .= $hRow['username'] . ", ";
                }
                $hOut = substr($hOut, 0, strlen($hOut) - 1);
                echo $hOut;
                

                【讨论】:

                • 哦,我的错,看来你是通过控制台运行的。
                • 我需要在 sql 中完成此操作,而不是在 php 或其他任何东西中(我实际上使用的是 c#)
                • 是的,我注意到它不是 PHP。
                猜你喜欢
                • 1970-01-01
                • 2020-09-29
                • 2023-03-28
                • 1970-01-01
                • 2012-02-01
                • 1970-01-01
                • 2020-01-28
                • 1970-01-01
                相关资源
                最近更新 更多