views:

17362

answers:

5

I have a simple query:

select * from countries

with the following results:

country_name
------------
Albania
Andorra
Antigua
.....

I would like to return the results in one row, so like this:

Albania, Andorra, Antigua, ...

Of course, I can write a PL/SQL function to do the job (I already did in Oracle 10g), but is there a nicer, preferably non-Oracle-specific solution (or may be a built-in function) for this task?

I would generally use it to avoid multiple rows in a sub-query, so if a person has more then one citizenship, I do not want her/him to be a duplicate in the list.

My question is based on the similar question on SQL server 2005.

UPDATE: My function looks like this:

CREATE OR REPLACE FUNCTION APPEND_FIELD (sqlstr in varchar2, sep in varchar2 ) return varchar2 is
ret varchar2(4000) := '';
TYPE cur_typ IS REF CURSOR;
rec cur_typ;
field varchar2(4000);
begin
     OPEN rec FOR sqlstr;
     LOOP
         FETCH rec INTO field;
      EXIT WHEN rec%NOTFOUND;
         ret := ret || field || sep;
     END LOOP;
     if length(ret) = 0 then
          RETURN '';
     else
          RETURN substr(ret,1,length(ret)-length(sep));
     end if;
end;
+3  A: 

The fastest way it is to use the Oracle collect function.

You can also do this:

select *
  2    from (
  3  select deptno,
  4         case when row_number() over (partition by deptno order by ename)=1
  5             then stragg(ename) over
  6                  (partition by deptno
  7                       order by ename
  8                         rows between unbounded preceding
  9                                  and unbounded following)
 10         end enames
 11    from emp
 12         )
 13   where enames is not null

Visit the site ask tom and search on 'stragg' or 'string concatenation' . Lots of examples. There is also a not-documented oracle function to achieve your needs.

tuinstoel
+6  A: 

This is the fastest way in Oracle:

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2196162600402#18313422264397

tuinstoel
See `string aggregation using collect` at http://www.oracle-developer.net/display.php?id=306 for the original post about this (same author).
Peter Lang
+1  A: 

Hi

I have always had to write some PL/SQL for this or I just concatenate a ',' to the field and copy into an editor and remove the CR from the list giving me the single line.

i.e.

select country_name||', ' country from countries

A little bit long winded both ways.

If you look at Ask Tom you will see loads of possible solutions but they all revert to type declarations and/or PL/SQL

Ask Tom

If anyone has a real simple way of doing this I would love to know as well.

Andrew Wood
+5  A: 

Here is a simple way without stragg or creating a function. It is referenced in Tom Kyte's blog. http://tkyte.blogspot.com/2006/08/evolution.html

create table countries ( country_name varchar2 (100));

insert into countries values ('Albania');

insert into countries values ('Andorra');

insert into countries values ('Antigua');


SELECT SUBSTR (SYS_CONNECT_BY_PATH (country_name , ','), 2) csv
      FROM (SELECT country_name , ROW_NUMBER () OVER (ORDER BY country_name ) rn,
                   COUNT (*) OVER () cnt
              FROM countries)
     WHERE rn = cnt
START WITH rn = 1
CONNECT BY rn = PRIOR rn + 1;

CSV                                                                             
--------------------------
Albania,Andorra,Antigua                                                         

1 row selected.
Daniel Emge
Nice short solution but a couple typos marred it.This line should read:FROM (SELECT country_name , ROW_NUMBER () OVER (ORDER BY country_name ) rn,
Stew S
A: 

You can use this as well:

select RTRIM(XMLAGG(XMLELEMENT(e,country_name || ',')).EXTRACT('//text()'),',') country_name from countries

Decci.7