【问题标题】:How do I create a comma-separated list using a SQL query?如何使用 SQL 查询创建逗号分隔的列表?
【发布时间】:2021-09-12 17:33:47
【问题描述】:

我有 3 个表叫:

  • 应用程序(ID、名称)
  • 资源(ID、名称)
  • ApplicationsResources(id、app_id、resource_id)

我想在 GUI 上显示所有资源名称的表格。在每一行的一个单元格中,我想列出该资源的所有应用程序(逗号分隔)。

所以问题是,在 SQL 中执行此操作的最佳方法是什么,因为我需要获取所有资源并且还需要获取每个资源的所有应用程序?

我是否首先运行 select * from resources 然后循环遍历每个资源并对每个资源执行单独的查询以获取该资源的应用程序列表?

有没有一种方法可以在一个查询中做到这一点?

【问题讨论】:

  • 我希望我的解决方案与数据库无关
  • @oo: 字符串处理与数据库无关。
  • 什么数据库产品和版本?
  • @prabhats.net:您可能是 SO 新手,因此请记住为有用的答案投票并接受对您最有帮助的答案。

标签: sql sql-server tsql string-aggregation


【解决方案1】:

MySQL

  SELECT r.name,
         GROUP_CONCAT(a.name SEPARATOR ',')
    FROM RESOURCES r
    JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
    JOIN APPLICATIONS a ON a.id = ar.app_id
GROUP BY r.name

SQL Server (2005+)

SELECT r.name,
       STUFF((SELECT ',' + a.name
               FROM APPLICATIONS a
               JOIN APPLICATIONRESOURCES ar ON ar.app_id = a.id
              WHERE ar.resource_id = r.id
           GROUP BY a.name
            FOR XML PATH(''), TYPE).value('text()[1]','NVARCHAR(max)'), 1, LEN(','), '')
 FROM RESOURCES r

SQL Server (2017+)

  SELECT r.name,
         STRING_AGG(a.name, ',')
    FROM RESOURCES r
    JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
    JOIN APPLICATIONS a ON a.id = ar.app_id
GROUP BY r.name

甲骨文

我推荐reading about string aggregation/concatentation in Oracle

【讨论】:

  • SQL Server 无缘无故地让事情变得困难
  • 另外值得注意的是group_concat默认为','分隔所以group_concat(a.name)等价于group_concat(a.name SEPARATOR ',')
  • SQLite 和 MySQL 一样,只是函数看起来像:GROUP_CONCAT(a.name, ', ')
【解决方案2】:

注意:

不推荐使用此方法,因为它会给出不正确或不确定的结果。
这已记录在 StackOverflowDBA

在 SQL Server 中使用 COALESCE 构建逗号分隔的字符串
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string

例子:

DECLARE @EmployeeList varchar(100)

SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') + 
   CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1

SELECT @EmployeeList

【讨论】:

  • 这似乎是比评分较高的答案更好的解决方案。简单而多才多艺。
  • 如果您想按部门(分组依据)列出 EmployeeList 怎么办?
  • @Gary:然后添加一个 GROUP BY 子句。
【解决方案3】:

我不知道是否有任何解决方案可以以与数据库无关的方式执行此操作,因为您很可能需要某种形式的字符串操作,而且这些操作通常因供应商而异。

对于 SQL Server 2005 及更高版本,您可以使用:

SELECT
     r.ID, r.Name,
     Resources = STUFF(
       (SELECT ','+a.Name
        FROM dbo.Applications a
        INNER JOIN dbo.ApplicationsResources ar ON ar.app_id = a.id
        WHERE ar.resource_id = r.id
        FOR XML PATH('')), 1, 1, '')
FROM
     dbo.Resources r

它使用 SQL Server 2005 FOR XML PATH 构造以逗号分隔的列表形式列出子项(给定资源的应用程序)。

马克

【讨论】:

  • 我相信您缺少一个“r”。 INNER JOIN 应该说“ON ar.app_id = a.id”。现在为我工作。谢谢。
  • @MagnusSmith:绝对!感谢您发现这个错字的“鹰眼”!固定。
【解决方案4】:

我相信你想要的是:

SELECT ItemName, GROUP_CONCAT(DepartmentId) FROM table_name GROUP BY ItemName

如果您使用的是 MySQL

参考

【讨论】:

  • GROUP_CONCAT 仅适用于 MySQL 和 SQLite。 OP 在评论中说他们正在使用 MS SQL Server 2008。
  • 发帖后才看到评论。我应该因为不相关而删除我的答案还是仅仅因为感兴趣而留下它?
  • ......多年后,但为了记录......我会留下它,它很有帮助并且 OP 的标题是模棱两可的。我正在寻找 MySQL 的解决方案,出现了这个问题(它被标记为 mysql)& 这个答案对我有帮助!
  • 你的答案出现在 mysql 的 serps 中。节省了我几个小时 - 感谢您留下它。
【解决方案5】:

假设 SQL Server:

表结构:

CREATE TABLE [dbo].[item_dept](
    [ItemName] char(20) NULL,
    [DepartmentID] int NULL   
)

查询:

SELECT ItemName,
       STUFF((SELECT ',' + rtrim(convert(char(10),DepartmentID))
        FROM   item_dept b
        WHERE  a.ItemName = b.ItemName
        FOR XML PATH('')),1,1,'') DepartmentID
FROM   item_dept a
GROUP BY ItemName

结果:

ItemName    DepartmentID
item1       21,13,9,36
item2       4,9,44

【讨论】:

  • +1 这行得通,但恕我直言,这个 SQL Server 解决方案是一种 hack,非常丑陋且不直观。不过不是你的错。 :)
  • 如果保证分隔项的顺序不是必需的,则可以使用自定义 CLR 聚合的另一种 SQL Server 解决方案。
【解决方案6】:

我想我们可以写成下面的方式来检索(下面的代码只是一个例子,请根据需要修改):

Create FUNCTION dbo.ufnGetEmployeeMultiple(@DepartmentID int)
RETURNS VARCHAR(1000) AS

BEGIN

DECLARE @Employeelist varchar(1000)

SELECT @Employeelist = COALESCE(@Employeelist + ', ', '') + E.LoginID
FROM humanresources.Employee E

Left JOIN humanresources.EmployeeDepartmentHistory H ON
E.BusinessEntityID = H.BusinessEntityID

INNER JOIN HumanResources.Department D ON
H.DepartmentID = D.DepartmentID

Where H.DepartmentID = @DepartmentID

Return @Employeelist

END

SELECT D.name as Department, dbo.ufnGetEmployeeMultiple (D.DepartmentID)as Employees
FROM HumanResources.Department D

SELECT Distinct (D.name) as Department, dbo.ufnGetEmployeeMultiple (D.DepartmentID) as 
Employees
FROM HumanResources.Department D

【讨论】:

    【解决方案7】:

    来自next version of SQL Server你将能够做到

    SELECT r.name,
           STRING_AGG(a.name, ',')
    FROM   RESOURCES r
           JOIN APPLICATIONSRESOURCES ar
             ON ar.resource_id = r.id
           JOIN APPLICATIONS a
             ON a.id = ar.app_id
    GROUP  BY r.name 
    

    对于该产品的早期版本,有多种不同的方法可以解决此问题。文章中有对它们的精彩评论:Concatenating Row Values in Transact-SQL

    • 当项目数未知时连接值

      • 递归 CTE 方法
      • 黑盒 XML 方法
      • 使用公共语言运行时
      • 带有递归的标量 UDF
      • 带有 WHILE 循环的表值 UDF
      • 动态 SQL
      • 光标方法
        .
    • 不可靠的方法

      • 带有 t-SQL 更新扩展的标量 UDF
      • 在 SELECT 中具有变量连接的标量 UDF

    【讨论】:

      【解决方案8】:

      MySQL

        SELECT r.name,
               GROUP_CONCAT(a.name SEPARATOR ',')
          FROM RESOURCES r
          JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
          JOIN APPLICATIONS a ON a.id = ar.app_id
      GROUP BY r.name
      

      **


      MS SQL 服务器

      SELECT r.name,
             STUFF((SELECT ','+ a.name
                     FROM APPLICATIONS a
                     JOIN APPLICATIONRESOURCES ar ON ar.app_id = a.id
                    WHERE ar.resource_id = r.id
                 GROUP BY a.name
                  FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '')
       FROM RESOURCES r
       GROUP BY deptno;
      

      甲骨文

        SELECT r.name,
               LISTAGG(a.name SEPARATOR ',') WITHIN GROUP (ORDER BY a.name)
        FROM RESOURCES r
              JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
              JOIN APPLICATIONS a ON a.id = ar.app_id
        GROUP BY r.name;
      

      【讨论】:

        【解决方案9】:

        为了不可知论,退后并弃船。

        Select a.name as a_name, r.name as r_name
          from ApplicationsResource ar, Applications a, Resources r
         where a.id = ar.app_id
           and r.id = ar.resource_id
         order by r.name, a.name;
        

        现在使用您的服务器编程语言连接 a_names 而 r_name 与上次相同。

        【讨论】:

          【解决方案10】:

          这将在 SQL Server 中完成:

          DECLARE @listStr VARCHAR(MAX)
          SELECT @listStr = COALESCE(@listStr+',' ,'') + Convert(nvarchar(8),DepartmentId)
          FROM Table
          SELECT @listStr
          

          【讨论】:

          • 它不会为每个 GROUP BY 执行此操作。为此,您需要在可能的重复问题stackoverflow.com/questions/1817985/… 中调整 marc_s 的答案(或查看 Kenneth 在此问题中的答案)
          【解决方案11】:

          没有办法以与 DB 无关的方式做到这一点。 所以你需要像这样获取整个数据集:

          select 
            r.name as ResName, 
            a.name as AppName
          from 
            Resouces as r, 
            Applications as a, 
            ApplicationsResources as ar
          where
            ar.app_id = a.id 
            and ar.resource_id = r.id
          

          然后在按ResName分组的同时AppName以编程方式连接AppName

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-05-10
            • 2015-11-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多