Single SQL Select for one-to-many relationship tables using delimiter in column - Oracle
I have two table开发者_开发技巧s with one-to-many relationship.
Table Person:
Id_person Name
-----------------------
1 Jack Black
2 Joe White
Table Telephone:
Id_telephone Id_person Number Type
----------------------------------------
5 1 333222111 Mobile
6 1 444333222 Fax
7 2 555444333 Mobile
Desired result of SQL SELECT statement:
Name Telephone_Numbers
---------------------------------------------
Jack Black Mobile: 333222111, Fax: 444333222
Joe White Mobile: 555444333
I found many answers to this question, but related to SQL Server, i have no idea how to accomplish this with Oracle Database 10g. I need to populate Gridview in ASP.NET with this result, so i want to use this SELECT statement with OracleCommand and ExecuteQuery().
Thanks for any hints.
in 11gR2 you may use listAgg
with person as
(
select 1 id_person, 'Jack Black' FullName from dual
union all
select 2 id_person, 'Joe White' FullName from dual
)
, telephone as
(
select 5 id_telephone , 1 Id_person, 333222111 phone_Number , 'Mobile' phone_type from dual
union all
select 6 id_telephone , 1 Id_person, 444333222 phone_Number , 'Fax' phone_type from dual
union all
select 7 id_telephone , 2 Id_person, 555444333 phone_Number , 'Mobile' phone_type from dual
)
select person.FullName
,listagg(telephone.phone_type || ': ' || telephone.phone_number , ', ') within group (order by person.FullName ) personToPhone
from person
inner join
telephone
on person.id_person = telephone.id_person
group by person.FullName ;
FULLNAME PERSONTOPHONE
---------- -----------------------------------
Jack Black Fax: 444333222, Mobile: 333222111
Joe White Mobile: 555444333
Look at this question for other approaches: comma-separated list as a result of select statement in Oracle
EDIT
for 10g you may use XMLAgg
with person as
(
select 1 id_person, 'Jack Black' FullName from dual
union all
select 2 id_person, 'Joe White' FullName from dual
)
, telephone as
(
select 5 id_telephone , 1 Id_person, 333222111 phone_Number , 'Mobile' phone_type from dual
union all
select 6 id_telephone , 1 Id_person, 444333222 phone_Number , 'Fax' phone_type from dual
union all
select 7 id_telephone , 2 Id_person, 555444333 phone_Number , 'Mobile' phone_type from dual
)
select person.FullName
,RTRIM(XMLAGG(XMLELEMENT(e,telephone.phone_type || ': ' || telephone.phone_number || ',')).EXTRACT('//text()'),',') AS TelephoneToType
from person
inner join
telephone
on person.id_person = telephone.id_person
group by person.FullName ;
FULLNAME TELEPHONETOTYPE
---------- ---------------------------------
Jack Black Mobile: 333222111,Fax: 444333222
Joe White Mobile: 555444333
(as Mark Baker did in the linked SO question)
Select P.Name
, 'Mobile: ' || Coalesce( Min( Case When T.Type = 'Mobile' Then Number End ),'' )
+ Coalesce( 'Fax: ' || Min( Case When T.Type = 'Fax' Then Number End ),'' )
From Person P
Join Telephone T
On T.Id_Person = P.Id_person
Group By P.Name
There is a list of possible solutions here
Had the same problem awhile back. All of the answers that I found said to use listagg, but client uses 10g. Create this function. Function is not mine found online.
create or replace
FUNCTION column_concat
(
p_cursor sys_refcursor,
p_del VARCHAR2
) RETURN VARCHAR2
IS
l_value VARCHAR2(32767);
l_result VARCHAR2(32767);
BEGIN
LOOP
FETCH p_cursor INTO l_value;
EXIT WHEN p_cursor%notfound;
IF l_result IS NOT NULL THEN
l_result := l_result || p_del;
END IF;
l_result := l_result || l_value;
END LOOP;
RETURN l_result;
END column_concat;
use it in Select like so:
select pers.name Names, column_concat(cursor(SELECT tel.type ||':' || tel.telnumber FROM person per, telephone tel Where per.id_person = tel.id_person and tel.id_person = pers.id_person), ',' ) Telephone_Numbers from person pers;
This should work for you. If you are on 11g, there is a built in aggregate function LISTAGG which is easier:
with data
as
(
select
p.id_person,
p.name,
t.type||': '||t.phone_number telephone_number,
row_number() over (partition by p.id_person order by t.type) rn,
count(*) over (partition by p.id_person) cnt
from
person p
inner join telephone t
on t.id_person = p.id_person
)
select
name,
ltrim(sys_connect_by_path(telephone_number,', '),', ') telephone_number
from data
where rn = cnt
start with rn = 1
connect by prior id_person = id_person and prior rn = rn-1
order by id_person
精彩评论