SQLITE: объединение строк в одну строку, если они совместно используют столбец - PullRequest
5 голосов
/ 23 октября 2011

Из предыдущего поста у меня есть следующее представление в 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;

Теперь, поскольку данные генерируются из отношения «многие ко многим» между Attendee и Company, яможно получить такие результаты, как:

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)

Ответы [ 3 ]

10 голосов
/ 23 октября 2011

Используйте для этого агрегатную функцию 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 значения исключаются из результата. Руководство:

объединение всех ненулевых значений

Порядок элементов в CompanyIds и Companies является произвольным, согласно руководству :

Порядок составных элементов произвольный.

Также обратите внимание, что «произвольный» - это не то же самое, что «случайный». group_concat, как и другие агрегатные функции, обрабатывает набор строк в полученном порядке. Без ORDER BY этот порядок определяется тем, какой план запроса выполняется. В таблицах реляционных баз данных нет естественного порядка (вы не можете полагаться на порядок вставки вообще ). Но оба экземпляра group_concat() в одном и том же SELECT списке обрабатывают строки в одинаковом порядке , так что 1-й идентификатор в CompanyIds соответствует 1-му имени в 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;

Руководство о (необязательных) порядковых номерах в ORDER BY:

Если выражение ORDER BY является константным целым числом K, то это выражение считается псевдонимом для K-го столбца набора результатов (столбцы нумеруются слева направо, начиная с 1).

Используйте список GROUP BY в качестве ведущих ORDER BY выражений для достижения наилучших результатов.

Ничего не делать с производной таблицей после упорядочения, которое может изменить его структуру (например, присоединение подзапроса к другой таблице и т. Д.)

Наконец, обратите внимание, что похожие агрегатные функции в других СУБД могут вести себя немного иначе. Связанный:

2 голосов
/ 26 октября 2011

Ответ от этого поста поможет вам превратиться

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

в

Name     | company-1 | company-2
---------+-----------+----------
Doe John | company A | company B
1 голос
/ 23 октября 2011

Я думаю, что внутренний выбор может помочь, как:

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;
...