Как вывести данные из одного столбца в одну строку через запятую?

Есть запрос, который выводит данные о заказах и пациентах:

select
    fn_pat_name_by_id(p.keyid) AS fio
    ,TO_CHAR(p.birthdate,'dd.mm.yyyy') AS bd
    ,p_pat.address(p.keyid,1) addr
    ,s.text
from
    research r
    ,order o
    ,service s
    ,patient p
where
    o.research_id = r.id
    and o.service_id = s.keyid
    and r.patient_id = p.keyid

Как вывести данные в виде ФИО, Дата рождения, Адрес, Услуги, где данные в столбце услуги надо вывести как все услуги пациента в одну строку через запятую?

Пробовал так, но не помогает:

select
    fn_pat_name_by_id(p.keyid) AS fio
    ,TO_CHAR(p.birthdate,'dd.mm.yyyy') AS bd
    ,p_pat.address(p.keyid,1) addr
    ,(select listagg(s.text, ',') within group (order by s.text) from service s where s.keyid = o.service_id) as usl
from
    research r
    ,order o
    ,service s
    ,patient p
where
    o.research_id = r.id
    and o.service_id = s.keyid
    and r.patient_id = p.keyid

И еще одно замечание, в столбце Услуги в итоге может получиться большой текст, поэтому нужен тип CLOB.


Ответы (2 шт):

Автор решения: Kolhoznik
SELECT
  fn_pat_name_by_id(p.keyid) AS fio
  ,TO_CHAR(p.birthdate,'dd.mm.yyyy') AS bd
  ,p_pat.address(p.keyid,1) addr
  ,listagg(s.text, ', ') within GROUP (ORDER BY s.text) AS usl
FROM
  research r
  ,order o
  ,patient p
  ,service s
WHERE
  o.research_id = r.id
  AND r.patient_id = p.keyid
  AND s.keyid = o.service_id
GROUP BY p.keyid, p.birthdate

Получилось так.

→ Ссылка
Автор решения: 0xdb

Начать решение надо с создания минимального воспроизводимого примера. При этом, следует абстрагироваться от бизнес логики реальной задачи. Идёт речь, о пациентах и их процедурах, или о студентах и их лекциях, для технической стороны вопроса это не играет абсолютно никакой роли.

В вопросе простая связь таблиц многие-ко-многим. Надо сгрупировать значения из одной таблицы и вывести к ним лист связанных значений из другой таблицы.

Подготовим таблицы:

create table foo (id int, name varchar2 (8), created date)
/
create table bar (id int, text varchar2 (8))
/
create table foobar (fooid int, barid int)
/

Заполним их исходными данными:

insert all 
    when 1=1 then into foo values (rn, 'name'||rn, date'2020-01-01'+rn)
    when 1=1 then into bar values (rn, 'text'||rn)
select rownum rn from dual connect by level<=3
/
insert into foobar 
    select foo.id, bar.id from foo, bar 
/

Теперь запрос, который выводит все данные, выглядит так:

select
    foo.name, foo.created, bar.text 
from foo, foobar, bar 
where foobar.fooid = foo.id 
and   foobar.barid = bar.id
/
NAME     CREATED             TEXT    
-------- ------------------- --------
name1    2020-01-02 00:00:00 text1   
name2    2020-01-03 00:00:00 text1   
name3    2020-01-04 00:00:00 text1   
[...]
name3    2020-01-04 00:00:00 text3   

В итоге, запрос, который выведет желаемый результат:

select
    foo.name, to_char (foo.created,'dd.mm.yyyy') created, 
    listagg (bar.text, ',') within group (order by bar.text) textlist 
from foo 
join foobar on foobar.fooid = foo.id 
join bar on bar.id = foobar.barid 
group by foo.name, foo.created 
/
NAME     CREATED    TEXTLIST                
-------- ---------- ------------------------
name1    02.01.2020 text1,text2,text3       
name2    03.01.2020 text1,text2,text3       
name3    04.01.2020 text1,text2,text3       

Всё очень просто и понятно всем. Любой может скопировать команды выше и воспроизвести пример. Когда техническое решение станет понятно, то не составит особого труда заменить имена таблиц и колонок их реальными именами.

→ Ссылка