18

I have data like this in a table

NAME PRICE
A    2
B    3
C    5
D    9
E    5

I want to display all the values in one row; for instance:

A,2|B,3|C,5|D,9|E,5|

How would I go about making a query that will give me a string like this in Oracle? I don't need it to be programmed into something; I just want a way to get that line to appear in the results so I can copy it over and paste it in a word document.

My Oracle version is 10.2.0.5.

Ben
  • 51,770
  • 36
  • 127
  • 149
Ramie
  • 1,171
  • 2
  • 16
  • 35
  • copy and paste it into another word? – Woot4Moo Jan 09 '13 at 17:37
  • Oracle Database 10g Release 2: 10.2.0.5 – Ramie Jan 09 '13 at 17:41
  • +1 for the question, this is beyond `listagg`.... – bonCodigo Jan 09 '13 at 17:58
  • 1
    @bonCodigo; the OP can't use LISTAGG() as this isn't 11g, however, it's not beyond the capabilities: `select listagg(name || ',' || price, '|') within group ( order by name ) from TABLE` – Ben Jan 09 '13 at 18:08
  • @Ben thanks for sharing that. Well I followed a different path with `Xmlagg` as I was using 11g. Any thoughts to improve it.. – bonCodigo Jan 09 '13 at 18:09
  • SOLVED: I just exported the values to CSV file and then created a program to concatenate and output to another file with that format... LOL thanks for trying to help though guys – Ramie Jan 09 '13 at 20:08

7 Answers7

15

I know this is a little late but try this:

SELECT LISTAGG(CONCAT(CONCAT(NAME,','),PRICE),'|') WITHIN GROUP (ORDER BY NAME) AS CONCATDATA
FROM your_table
Kyle
  • 2,645
  • 3
  • 21
  • 8
  • 3
    The OP was on 10g and `listagg()` was introduced in 11g. But this answer makes the whole post a better round up of string aggregation techniques. – APC Aug 26 '14 at 07:05
13

-- Oracle 10g --

SELECT deptno, WM_CONCAT(ename) AS employees
  FROM   scott.emp
GROUP BY deptno;

Output:
     10  CLARK,MILLER,KING
     20  SMITH,FORD,ADAMS,SCOTT,JONES
     30  ALLEN,JAMES,TURNER,BLAKE,MARTIN,WARD
Art
  • 5,616
  • 1
  • 20
  • 22
  • This works beautifully, plus it's easy to remember how to use. You can throw out duplicates with `WM_CONCAT(distinct ename)`, though I doubt you can sort them. And, unfortunately, it's not [officially documented](http://psoug.org/definition/wm_concat.htm). – bart Oct 07 '14 at 07:04
  • the question was different – Toolkit Jan 19 '17 at 16:43
3

Usually when I need something like that quickly and I want to stay on SQL without using PL/SQL, I use something similar to the hack below:

select sys_connect_by_path(col, ', ') as concat
from
(
  select 'E' as col, 1 as seq from dual
  union
  select 'F', 2 from dual
  union
  select 'G', 3 from dual
)
where seq = 3
start with seq = 1
connect by prior seq+1 = seq

It's a hierarchical query which uses the "sys_connect_by_path" special function, which is designed to get the "path" from a parent to a child.

What we are doing is simulating that the record with seq=1 is the parent of the record with seq=2 and so fourth, and then getting the full path of the last child (in this case, record with seq = 3), which will effectively be a concatenation of all the "col" columns

Adapted to your case:

select sys_connect_by_path(to_clob(col), '|') as concat
from
(
  select name || ',' || price as col, rownum as seq, max(rownum) over (partition by 1) as max_seq
  from
  (
   /* Simulating your table */
    select 'A' as name, 2 as price from dual
    union
    select 'B' as name, 3 as price from dual
    union
    select 'C' as name, 5 as price from dual
    union
    select 'D' as name, 9 as price from dual
    union
    select 'E' as name, 5 as price from dual
  )
)
where seq = max_seq
start with seq = 1
connect by prior seq+1 = seq

Result is: |A,2|B,3|C,5|D,9|E,5

beder
  • 1,086
  • 5
  • 10
2

As you're in Oracle 10g you can't use the excellent listagg(). However, there are numerous other string aggregation techniques.

There's no particular need for all the complicated stuff. Assuming the following table

create table a ( NAME varchar2(1), PRICE number);
insert all
into a values ('A',    2)
into a values ('B',    3)
into a values ('C',    5)
into a values ('D',    9)
into a values ('E',    5)
select * from dual

The unsupported function wm_concat should be sufficient:

select replace(replace(wm_concat (name || '#' || price), ',', '|'), '#', ',')
  from a;

REPLACE(REPLACE(WM_CONCAT(NAME||'#'||PRICE),',','|'),'#',',')
--------------------------------------------------------------------------------
A,2|B,3|C,5|D,9|E,5

But, you could also alter Tom Kyte's stragg, also in the above link, to do it without the replace functions.

Ben
  • 51,770
  • 36
  • 127
  • 149
2

Here is another approach, using model clause:

-- sample of data from your question
with t1(NAME1, PRICE) as(
   select 'A',    2 from dual union all
   select 'B',    3 from dual union all
   select 'C',    5 from dual union all
   select 'D',    9 from dual union all
   select 'E',    5 from dual
) -- the query
 select Res
  from (select name1
             , price
             , rn
             , res
         from t1
         model
         dimension by (row_number() over(order by name1) rn)
         measures (name1, price, cast(null as varchar2(101)) as res)
         (res[rn] order by rn desc = name1[cv()] || ',' || price[cv()] || '|' ||  res[cv() + 1])
       )
where rn = 1  

Result:

RES
----------------------
A,2|B,3|C,5|D,9|E,5| 

SQLFiddle Example

Nick Krasnov
  • 26,886
  • 6
  • 61
  • 78
1

Something like the following, which is grossly inefficient and untested.

    create function foo returning varchar2  as  
    (    
        declare bar varchar2(8000) --arbitrary number
        CURSOR cur IS
        SELECT name,price  
        from my_table  
        LOOP

    FETCH cur INTO r;

    EXIT WHEN cur%NOTFOUND;

       bar:= r.name|| ',' ||r.price || '|'

  END LOOP;  
  dbms_output.put_line(bar);
       return bar
    )  
Woot4Moo
  • 23,987
  • 16
  • 94
  • 151
  • what does `FETCH cur INTO r;` do? – Ramie Jan 09 '13 at 17:46
  • places the results of the `cursor` defined as `cur` into a variable called `r`. Notice how i concat `bar` and reference your columns via `r.name` and `r.price` – Woot4Moo Jan 09 '13 at 17:48
  • When i make that function, how do i execute it? Do i just run that function in the sql query window? – Ramie Jan 09 '13 at 17:52
  • @Ramie in sql developer you can right click and run the function. – Woot4Moo Jan 09 '13 at 17:55
  • Can i change the number inside the varchar2, because i ran your query and it says that its finished executing but no results. create function f323oo returning varchar2 as ( declare bar varchar2(8000) --arbitrary number CURSOR cur IS SELECT CURRENCY, ONE from mkt.view_me LOOP FETCH cur INTO r; EXIT WHEN cur%NOTFOUND; bar:= r.CURRENCY|| ',' ||r.ONE || '|' END LOOP; return bar ) – Ramie Jan 09 '13 at 18:06
  • @Ramie you may change the number to I believe to `65536` as a maximum. If it doesn't return into anything try the code I just posted. I added a `dbms_output.put_line` which is equivalent to a system print. – Woot4Moo Jan 09 '13 at 18:09
  • it didn't work as it showed up with an empty result, sorry :( – Ramie Jan 09 '13 at 18:27
  • But works in any version +1 – Lluis Martinez Sep 23 '21 at 12:50
1

Managed to get till here using xmlagg: using oracle 11G from sql fiddle.

Data Table:

COL1    COL2    COL3
1       0       0
1       1       1
2       0       0
3       0       0
3       1       0


SELECT
    RTRIM(REPLACE(REPLACE(
      XMLAgg(XMLElement("x", col1,',', col2, col3)

ORDER BY col1), '<x>'), '</x>', '|')) AS COLS
  FROM ab
;

Results:

COLS
1,00| 3,00| 2,00| 1,11| 3,10|

* SQLFIDDLE DEMO

bonCodigo
  • 14,268
  • 1
  • 48
  • 91
  • @Woot4Moo that's why I said I only managed to get till here. I didn't post for an upvote but for another method if possible to adapot. However didn't expect a downvote... :$ – bonCodigo Jan 09 '13 at 18:07
  • Such is life I suppose. I felt it was down vote worthy, as it doesn't provide much detail around `XMLAgg` and how this is efficient – Woot4Moo Jan 09 '13 at 18:10
  • @WootMoo I Don't think it's my answer is wrong. I have just missed out `|` as a separator. I am using different sample data, but my logic is correct. – bonCodigo Jan 09 '13 at 18:20
  • @Ramie please take a look at this answer. It does support `10g` as well. Performance wise it's pretty fast. – bonCodigo Jan 09 '13 at 18:21
  • make it work with 2 columns and ill test it, that way i can just replace variables – Ramie Jan 09 '13 at 18:31
  • @Ramie Good lord! just remove col3 from the xmlelement! [here](http://sqlfiddle.com/#!4/bfd9a/27) – bonCodigo Jan 09 '13 at 18:32
  • @Ramie all good so far at your side? Since I was challenged....here I have gotten to test this with many heavy machines. It runs very well. Looking forward to hear from you. And let me know if you need further clarifications or changes ;) – bonCodigo Jan 09 '13 at 19:52