Home » SQL & PL/SQL » SQL & PL/SQL » A query for this
A query for this [message #17291] Tue, 04 January 2005 15:07 Go to next message
ORCL_NEWBIE
Messages: 5
Registered: October 2002
Junior Member
Hi,
I am new to Oracle. Can someone help me with this. It will be of great help

P_CD XP_CD LEVEL

1111111 2 1
1111111 1 1
1111111 3 1
1111111 21 2
1111111 13 2
1111111 11 2
1111111 12 2
1111111 32 2
1111111 23 2
1111111 22 2
1111111 31 2
1111111 39 2
1111111 132 3
1111111 131 3
1111111 124 3
1111111 129 3

I want the output like this

P_CD LEVEL XP_CD
1111111 1 2,1,3
1111111 2 21,13,11,12,32,23,22,31,39
1111111 3 132,124,129

Please help.

Thanks in advance
Re: A query for this [message #17292 is a reply to message #17291] Tue, 04 January 2005 17:25 Go to previous messageGo to next message
Barbara Boehmer
Messages: 9090
Registered: November 2002
Location: California, USA
Senior Member
If you search these forums, you will find various examples of various methods and links to various demonstrations and explanations. I have demonstrated four of them below. Since you cannot have a column named level, because it is an Oracle reserved word, I have used lvl for the name of that column. Which method you choose depends on whether you want the concatenated values ordered or not, whether this is a one time thing or something you need to do frequently with different tables, where a generic functon would be more convenient, whether you know the maximum number of values in advance, and whether you are restricted to just sql, and which version of Oracle you are using.

-- test data:
scott@ORA92> select * from your_table
  2  /

      P_CD                           XP_CD        LVL
---------- ------------------------------- ----------
   1111111                               2          1
   1111111                               1          1
   1111111                               3          1
   1111111                              21          2
   1111111                              13          2
   1111111                              11          2
   1111111                              12          2
   1111111                              32          2
   1111111                              23          2
   1111111                              22          2
   1111111                              31          2
   1111111                              39          2
   1111111                             132          3
   1111111                             131          3
   1111111                             124          3
   1111111                             129          3

16 rows selected.


-- William Robertson's method
-- (sql only and orders concatenated values)
-- (requires at least Oracle 9i)
scott@ORA92> select p_cd, lvl,
  2  	    max (substr (sys_connect_by_path (xp_cd, ','), 2))
  3  	      keep (dense_rank last order by curr) as xp_cds
  4  from   (select p_cd, lvl, xp_cd,
  5  		    row_number () over (partition by p_cd, lvl order by xp_cd) as curr,
  6  		    row_number () over (partition by p_cd, lvl order by xp_cd) -1 as prev
  7  	     from   your_table)
  8  start  with curr = 1
  9  connect by prior curr = prev and prior p_cd = p_cd and prior lvl = lvl
 10  group  by p_cd, lvl
 11  /

      P_CD        LVL XP_CDS
---------- ---------- ------------------------------
   1111111          1 1,2,3
   1111111          2 11,12,13,21,22,23,31,32,39
   1111111          3 124,129,131,132


-- Tom Kyte's method
-- (generic, does not order concatenated values)
-- (requires at least Oracle 9i)
scott@ORA92> create or replace type string_agg_type as object
  2  (
  3  	total varchar2(4000),
  4  
  5  	static function
  6  	     ODCIAggregateInitialize(sctx IN OUT string_agg_type )
  7  	     return number,
  8  
  9  	member function
 10  	     ODCIAggregateIterate(self IN OUT string_agg_type ,
 11  				  value IN varchar2 )
 12  	     return number,
 13  
 14  	member function
 15  	     ODCIAggregateTerminate(self IN string_agg_type,
 16  				    returnValue OUT  varchar2,
 17  				    flags IN number)
 18  	     return number,
 19  
 20  	member function
 21  	     ODCIAggregateMerge(self IN OUT string_agg_type,
 22  				ctx2 IN string_agg_type)
 23  	     return number
 24  );
 25  /

Type created.

scott@ORA92> create or replace type body string_agg_type
  2  is
  3  
  4  static function ODCIAggregateInitialize(sctx IN OUT string_agg_type)
  5  return number
  6  is
  7  begin
  8  	 sctx := string_agg_type( null );
  9  	 return ODCIConst.Success;
 10  end;
 11  
 12  member function ODCIAggregateIterate(self IN OUT string_agg_type,
 13  					  value IN varchar2 )
 14  return number
 15  is
 16  begin
 17  	 self.total := self.total
 18  	 || ','
 19  	 || value;
 20  	 return ODCIConst.Success;
 21  end;
 22  
 23  member function ODCIAggregateTerminate(self IN string_agg_type,
 24  					    returnValue OUT varchar2,
 25  					    flags IN number)
 26  return number
 27  is
 28  begin
 29  	 returnValue := ltrim(self.total,',');
 30  	 return ODCIConst.Success;
 31  end;
 32  
 33  member function ODCIAggregateMerge(self IN OUT string_agg_type,
 34  					ctx2 IN string_agg_type)
 35  return number
 36  is
 37  begin
 38  	 self.total := self.total || ctx2.total;
 39  	 return ODCIConst.Success;
 40  end;
 41  
 42  
 43  end;
 44  /

Type body created.

scott@ORA92> CREATE or replace
  2  FUNCTION stragg(input varchar2 )
  3  RETURN varchar2
  4  PARALLEL_ENABLE AGGREGATE USING string_agg_type;
  5  /

Function created.

scott@ORA92> SELECT p_cd, lvl, STRAGG (xp_cd) as xp_cds
  2  FROM   your_table
  3  GROUP  BY p_cd, lvl
  4  /

      P_CD        LVL XP_CDS
---------- ---------- ------------------------------
   1111111          1 2,1,3
   1111111          2 21,32,22,39,31,23,12,13,11
   1111111          3 132,131,124,129


-- another method:
-- (specific to situation, orders concatenated values)
-- (works in Oracle 8i and works in 8.0 without order by clause)
scott@ORA92> create or replace function your_func
  2    (p_p_cd	in your_table.p_cd%type,
  3  	p_lvl in your_table.lvl%type)
  4    return varchar2
  5  as
  6    v_result    varchar2(32767);
  7  begin
  8    for rec in
  9  	 (select xp_cd
 10  	  from	 your_table
 11  	  where  p_cd = p_p_cd
 12  	  and	 lvl = p_lvl
 13  	  order  by xp_cd)
 14    loop
 15  	 v_result := v_result || ',' || rec.xp_cd;
 16    end loop;
 17    return substr (v_result, 2);
 18  end;
 19  /

Function created.

scott@ORA92> show errors
No errors.
scott@ORA92> select p_cd, lvl,
  2  	    your_func (p_cd, lvl) as xp_cds
  3  from   your_table
  4  group  by p_cd, lvl
  5  /

      P_CD        LVL XP_CDS
---------- ---------- ------------------------------
   1111111          1 1,2,3
   1111111          2 11,12,13,21,22,23,31,32,39
   1111111          3 124,129,131,132


-- another method
-- (requires that maximum number of concatenated values are known)
scott@ORA92> -- requires Oracle 9i
scott@ORA92> select p_cd, lvl,
  2  	    rtrim (max (decode (rn, 1, xp_cd))
  3  	    || ',' || max (decode (rn, 2, xp_cd))
  4  	    || ',' || max (decode (rn, 3, xp_cd))
  5  	    || ',' || max (decode (rn, 4, xp_cd))
  6  	    || ',' || max (decode (rn, 5, xp_cd))
  7  	    || ',' || max (decode (rn, 6, xp_cd))
  8  	    || ',' || max (decode (rn, 7, xp_cd))
  9  	    || ',' || max (decode (rn, 8, xp_cd))
 10  	    || ',' || max (decode (rn, 9, xp_cd))
 11  	    -- and so on until maximum number of values of xp_cd
 12  	    , ',') as xp_cds
 13  from   (select p_cd, xp_cd, lvl,
 14  		    row_number () over (partition by p_cd, lvl order by xp_cd) as rn
 15  	     from   your_table)
 16  group  by p_cd, lvl
 17  /

      P_CD        LVL XP_CDS
---------- ---------- ------------------------------
   1111111          1 1,2,3
   1111111          2 11,12,13,21,22,23,31,32,39
   1111111          3 124,129,131,132

scott@ORA92> 
Re: A query for this [message #17298 is a reply to message #17292] Wed, 05 January 2005 01:11 Go to previous messageGo to next message
Padders
Messages: 79
Registered: January 2004
Member
If you had 10g you could lose the GROUP BY from Will's method (and hence a sort) in favour of CONNECT_BY_ISLEAF.
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options

SQL> SELECT p_cd, lvl, SUBSTR (SYS_CONNECT_BY_PATH (xp_cd, ','), 2) xp_cds
  2  FROM  (SELECT p_cd, lvl, xp_cd,
  3                ROW_NUMBER () OVER (
  4         PARTITION BY p_cd, lvl ORDER BY xp_cd) n
  5         FROM   your_table)
  6  WHERE  CONNECT_BY_ISLEAF = 1
  7  START WITH n = 1
  8  CONNECT BY PRIOR n + 1 = n AND PRIOR p_cd = p_cd AND PRIOR lvl = lvl;

      P_CD        LVL XP_CDS
---------- ---------- ----------------------------------------
   1111111          1 1,2,3
   1111111          2 11,12,13,21,22,23,31,32,39
   1111111          3 124,129,131,132
 
SQL> 

Mind you if you had 10g you could also consider Adrian Billington's method of using COLLECT aggregate together with a user-defined function.
SQL> CREATE TYPE VARCHAR2_TABLE AS TABLE OF VARCHAR2 (4000);
  2  /

Type created.

SQL> CREATE FUNCTION tabletostr (
  2    p_table IN VARCHAR2_TABLE,
  3    p_delimiter IN VARCHAR2 := ',')
  4    RETURN VARCHAR2
  5  IS
  6    v_str VARCHAR2 (4000);
  7  BEGIN
  8    FOR i IN 1 .. p_table.COUNT LOOP
  9      v_str := v_str || p_delimiter || p_table (i);
 10    END LOOP;
 11    RETURN v_str;
 12  END;
 13  /

Function created.

SQL> SELECT p_cd, lvl, 
  2           SUBSTR (tabletostr (CAST (
  3             COLLECT (TO_CHAR (xp_cd)) 
  4     AS VARCHAR2_TABLE)), 2) xp_cds
  5  FROM   your_table  
  6  GROUP BY p_cd, lvl;

      P_CD        LVL XP_CDS
---------- ---------- ----------------------------------------
   1111111          1 2,1,3
   1111111          2 21,13,11,12,32,23,22,31,39
   1111111          3 132,131,124,129

SQL> 
Re: A query for this [message #17303 is a reply to message #17291] Wed, 05 January 2005 05:59 Go to previous message
ORCL_NEWBIE
Messages: 5
Registered: October 2002
Junior Member
Thank you very much for the query
Previous Topic: Dynamic SQL problem: urgent solution required.please.!
Next Topic: dynamic sql
Goto Forum:
  


Current Time: Thu Apr 25 17:29:37 CDT 2024