SQLITE:如果它们共享一列,则将行合并为单行

Posted

技术标签:

【中文标题】SQLITE:如果它们共享一列,则将行合并为单行【英文标题】:SQLITE: merging rows into single row if they share a column 【发布时间】:2011-12-13 14:55:33 【问题描述】:

从上一篇文章中,我在 sqlite3 中有以下视图:

CREATE View AttendeeTableView AS

SELECT  (LastName || " " || FirstName) as AttendeeName,  
        CompanyName, 
        PhotoURI,
        CompanyAttendeeRelation.CompanyId,
        CompanyAttendeeRelation.AttendeeId 

FROM    Attendee 
JOIN    CompanyAttendeeRelation on CompanyAttendeeRelation.AttendeeId = Attendee.AttendeeId 

ORDER BY LastName;

现在,由于数据是从AttendeeCompany 之间的多对多关系生成的,我可以得到如下结果:

Doe John | company A | johnPic.png | 1 | 15
Doe John | company B | johnPic.png | 2 | 15

我想做的是,如果有不止一家公司(如上),创建一个输出的查询:

Doe John | company A company B | johnPic.png | 1 2 | 15

还有一个输出:

Doe John | company A | company B | johnPic.png | 1 | 2 | 15

所以我基本上需要知道如何合并具有不同行的特定列 该表中的值。

有什么想法吗?

以防万一,第一个查询中的company A company B显然是文本连接,即类似于(row1.CompanyName || " " || row2.CompanyName)的内容

【问题讨论】:

【参考方案1】:

为此使用聚合函数group_concat(X)

SELECT (a.LastName || " " || a.FirstName) AS AttendeeName
     , a.PhotoURI
     , group_concat(c.CompanyName) AS Companies
     , group_concat(c.CompanyId)   AS CompanyIds
FROM   Attendee AS a
JOIN   CompanyAttendeeRelation AS ca ON ca.AttendeeId = a.AttendeeId
JOIN   Company                 AS c  ON c.CompanyId = ca.CompanyId
GROUP  BY a.LastName, a.Firstname, a.PhotoURI;

(使用表格别名使其更短且更易于阅读。)

NULL 值从结果中排除。 The manual:

所有非空值的串联

CompanyIdsCompanies中的元素顺序是任意的,根据the manual:

连接元素的顺序是任意的。

另请注意,“任意”与“随机”不同。 group_concat 与其他聚合函数一样,按接收顺序处理行集。如果没有任何ORDER BY,则该顺序由执行的任何查询计划决定。关系数据库的表中没有自然顺序(您不能完全依赖插入顺序)。但是group_concat() 的两个实例在同一SELECT 中以相同 的顺序列出进程行,因此CompanyIds 中的第一个ID 对应于Companies 中的第一个名称。

您可以在子查询中使用ORDER BY 强加您的订单。这是一个实现细节,但极不可能改变。喜欢:

SELECT (LastName || " " || FirstName) AS AttendeeName
     , PhotoURI
     , group_concat(CompanyName) AS Companies
     , group_concat(CompanyId)   AS CompanyIds
FROM  (
   SELECT a.LastName, a.FirstName, a.PhotoURI, c.CompanyName, c.CompanyId
   FROM   Attendee AS a
   JOIN   CompanyAttendeeRelation AS ca ON ca.AttendeeId = a.AttendeeId
   JOIN   Company                 AS c  ON c.CompanyId = ca.CompanyId
   ORDER  BY 1,2,3,4,5  -- or whatever you need
   ) AS sub
GROUP  BY LastName, Firstname, PhotoURI;

The manual 关于ORDER BY 中的(可选)序数:

如果 ORDER BY 表达式是一个常数整数 K,则该表达式被视为结果集第 K 列的别名(列从左到右从 1 开始编号)。

使用GROUP BY 列表作为前导ORDER BY 表达式以获得最佳结果。

排序后不要对派生表做任何可能重新排列它的事情(比如将子查询连接到另一个表等)

最后,请注意,其他 RDBMS 中类似的聚合函数的行为可能略有不同。相关:

Concatenate multiple result rows of one column into one, group by another column GROUP_CONCAT ORDER BY

【讨论】:

手册说The order of the concatenated elements is arbitrary。这是否意味着串联的CompanyIds 不一定按照Companies 的顺序匹配? @Jason:我添加了更多来解决这个问题(除其他外)。【参考方案2】:

this post的回答会帮你转

Name     | company
---------+----------
Doe John | company A
Doe John | company B

进入

Name     | company-1 | company-2
---------+-----------+----------
Doe John | company A | company B

【讨论】:

【参考方案3】:

我认为内部选择可能会有所帮助,例如:

CREATE View AttendeeTableView AS

SELECT  (LastName || " " || FirstName) as AttendeeName,  

(
  select CompanyName
FROM    Attendee A_innner 
JOIN    CompanyAttendeeRelation CAR  /* is this where company name is? */  
ON      on CAR.AttendeeId = A.AttendeeId /* if not remove the joins and CAR */
WHERE   A_inner.last_name = A_outer.last_name and
        A_inner.first_name = A_outer.first_name
),
PhotoURI,
CAR.CompanyId,
CAR.AttendeeId 


FROM    Attendee A_outer 
JOIN    CompanyAttendeeRelation CAR_outer  
ON      on CAR_outer.AttendeeId = A_outer.AttendeeId 

GROUP by LastName,FirstName
ORDER BY LastName, FirstName;

【讨论】:

以上是关于SQLITE:如果它们共享一列,则将行合并为单行的主要内容,如果未能解决你的问题,请参考以下文章

Python - 如果 DOB 和 Address1 和 Address2 和 PostCode 为 NULL,则将行作为 Badrecord 移动到新数据帧

将行拆分为多行 Oracle

如果行具有匹配的 ID,则将多行中的值合并为一行(单独的单元格)。如果 ID 只存在一次,则为 NULL 值

如何通过在R语言中对相同的变量进行分组来将行合并为单列

SQL 将行合并为列

631D Messenger