Link to home
Start Free TrialLog in
Avatar of FamousMortimer
FamousMortimerFlag for United States of America

asked on

Need Elegant SQL query solution (DB2)

Hi Experts,
I have a DB2 table like this:

JOB | Sequence | TXT
123 |       1        | this is an ex
123 |       2        |ample to sho
123 |       3        |w my data.
124 |       1        |one line here
125 |       1        |another tes
125 |       2        |t

My goal is to retrieve a query with all of the text as one field so the example above would return 3 records.

123 | this is an example to show my data
124 | one line here
125 | another test

Any help is greatly appreciated.  Oh and I absolutely cannot change this table... and have no idea why it was done this way.

Thanks
-Jeremy
Avatar of sathyaram_s
sathyaram_s
Flag of United Kingdom of Great Britain and Northern Ireland image

If you are on 9.7 FP4 you can use listagg

drop table rec ;

CREATE TABLE rec
(
  snum      INTEGER,        -- sentence number
  wordnum   INTEGER,        -- word number in the sentence
  word      VARCHAR(100)    -- word being saved
);

INSERT INTO rec
VALUES (1, 1, 'This'), (1, 2, 'is'), (1, 3, 'a'), (1, 4, 'fine'), (1, 5, 'example');


WITH rquery (snum, wordnum, sentence)
AS
(
  SELECT base.snum, base.wordnum, base.word
  FROM rec base
  WHERE wordnum = 1

  UNION ALL

  SELECT t1.snum, t1.wordnum, sentence || ' ' || t1.word
  FROM rquery t0, rec t1
  WHERE t0. snum = t1. snum
    AND t0.wordnum + 1 = t1.wordnum
)
SELECT *
FROM rquery
WHERE wordnum = (SELECT max(wordnum) FROM rquery);


 SELECT snum ,
LISTAGG(word,' ')       WITHIN GROUP(ORDER BY wordnum)
 FROM rec
      GROUP BY snum ;

Open in new window

Avatar of petr_hlucin
petr_hlucin

Since I don't know DB2 in depth I'm providing only 2 possible theoretical solutions:

1) Define custom aggregation function (e.g. CONCATE_AGGREGATE) which would concatenate the strings and run the following query:
SELECT CONCATENATE_AGGREGATE(TXT)
FROM  table
GROUP BY JOB
ORDER BY JOB, Sequence

I'm not sure whether DB2 supports it, PostgreSQL does.

2) Define a function with cursor returning such table, e.g. (pseudocode follows):
DEFINE CURSOR c1 FOR
SELECT * FROM table;
VARCHAR curr_txt;
INT curr_job;
BEGIN
OPEN c1;
WHILE FETCH(c1) INTO row DO
  IF row.JOB = curr_job THEN
    curr_txt := curr_txt + row.TXT;
  ELSE
    RETURN NEXT (curr_job, curr_txt);
    curr_txt := row.TXT;
    curr_job := row.JOB;
  END IF;
END WHILE
END;

Maybe somebody will complete this with DB2-specific stuff.
Avatar of Member_2_2484401
In case you don't have the LISTAGG function, the following code works for me:

select *
  from rec

SNUM         WORDNUM   WORD   
   1               1   This   
   1               2   is     
   1               3   a      
   1               4   fine   
   1               5   example
   2               1   Yet    
   2               2   another
   2               3   example
   2               4   blah1  
   2               5   blah2  
   2               6   blah3  

WITH rquery (snum, wordnum, sentence) as (
 SELECT base.snum, base.wordnum, base.word
    FROM rec base
   WHERE wordnum = 1

  UNION ALL

  SELECT t1.snum, t1.wordnum, sentence || ' ' || t1.word
    FROM rquery t0
    join rec t1
      on t0. snum = t1. snum
     AND t0.wordnum + 1 = t1.wordnum
)
select r1.SNUM,
       r1.SENTENCE
  from rquery r1             
 where r1.wordnum = (        
       select max(r2.wordnum)
       from rquery r2         
      where r2.snum = r1.snum)

SNUM   SENTENCE                             
   1   This is a fine example               
   2   Yet another example blah1 blah2 blah3

Open in new window


HTH,
DaveSlash
Avatar of FamousMortimer

ASKER

Thanks everyone for the quick responses.  I should have mentioned that i am a newbie at this and need a little more hand holding.  If someone would be so kind as to provide me a working example how to accomplish this using my example table that would be great.

My table is FILE110.DCTXT and the field names are JOB, SEQ, TXT

Thanks in advance
WITH rquery (snum, wordnum, sentence)
AS
(
  SELECT base.job, base.seq, base.txt
  FROM FILE110.DCTXT base
  WHERE wordnum = 1

  UNION ALL

  SELECT t1.job, t1.seq, sentence || ' ' || t1.txt
  FROM rquery t0, FILE110.DCTXT t1
  WHERE t0. snum = t1. job
    AND t0.wordnum + 1 = t1.seq
)
SELECT *
FROM rquery
WHERE wordnum = (SELECT max(wordnum) FROM rquery);


 SELECT snum ,
LISTAGG(word,' ')       WITHIN GROUP(ORDER BY wordnum)
 FROM rec
      GROUP BY snum ;
WITH rquery (job, seq, sentence) as (
 SELECT base.job, base.seq, base.text
    FROM FILE110.DCTXT base
   WHERE seq = 1

  UNION ALL

  SELECT t1.job, t1.seq, sentence || ' ' || t1.text
    FROM rquery t0
    join FILE110.DCTXT t1
      on t0. job = t1. job
     AND t0.seq + 1 = t1.seq
)
select r1.job,
       r1.SENTENCE
  from rquery r1             
 where r1.seq = (        
       select max(r2.seq)
       from rquery r2         
      where r2.job = r1.job)

Open in new window

OK, LISTAGG is not working for me, I do not know what version.

I was able to use and modify your code DaveSlash, however it is only returning the first and second in the sequence, any more than two does not concat.  Here is what i am using:

WITH rquery (snum, wordnum, sentence) as (
 SELECT base.TXJOB, base.TXSEQ, base.TXTXT
    FROM FILE110.DCTXT base
   WHERE TXSEQ = 1

  UNION ALL

  SELECT t1.TXJOB, t1.TXSEQ, sentence || ' ' || t1.TXTXT
    FROM rquery t0
    join FILE110.DCTXT t1
      on t0. snum = t1. TXJOB
     AND t0.wordnum + 1 = t1.TXSEQ
)
select r1.SNUM,
       r1.SENTENCE
  from rquery r1             
 where r1.wordnum = (        
       select max(r2.wordnum)
       from rquery r2         
      where r2.snum = r1.snum)

Open in new window

Can you see if this one works

WITH rquery (snum, wordnum, sentence)
AS
(
  SELECT base.snum, base.wordnum, base.word
  FROM rec base
  WHERE wordnum = 1

  UNION ALL

  SELECT t1.snum, t1.wordnum, sentence || ' ' || t1.word
  FROM rquery t0, rec t1
  WHERE t0. snum = t1. snum
    AND t0.wordnum + 1 = t1.wordnum
)
SELECT *
FROM rquery
WHERE wordnum = (SELECT max(wordnum) FROM rquery);

Open in new window

I get the same result as my previous post... it only joins the first two in the sequence
ASKER CERTIFIED SOLUTION
Avatar of sathyaram_s
sathyaram_s
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Beautiful... and elegant as requested