Skip to Main Content
  • Questions
  • Converting column number values into array number values in SQL

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, siva.

Asked: June 07, 2019 - 8:03 pm UTC

Last updated: May 09, 2024 - 10:19 am UTC

Version: 12

Viewed 10K+ times! This question is

You Asked

I have a table like below.

create table t2 ( id varchar2(1),val number) ;

insert into t2 values ('a',1);
insert into t2 values ('a',2);
insert into t2 values ('a',3);
insert into t2 values ('a',4);

insert into t2 values ('b',1);
insert into t2 values ('b',2);
insert into t2 values ('b',3);

insert into t2 values ('c',1);
insert into t2 values ('c',2);
insert into t2 values ('c',4);

insert into t2 values ('d',1);
insert into t2 values ('d',2);


we have to print o/p like below.

id   x 
---  -------
a    1,2,3,4
b    1,2,3
c    1,2,4
d    1,2


this can achieve by below query

select id,LISTAGG(val, ',') WITHIN GROUP (ORDER BY val ) as x 
from t2
group by id


Here x column is character datatype .But i need to convert this to varray of number / nested table of number ( not varray/nestedtable of character ).

i tried like below

CREATE TYPE varchar_TT AS TABLE OF varchar(10);

with z as (            
 select id,varchar_TT(LISTAGG(val, ',') WITHIN GROUP (ORDER BY val)) as x ,varchar_TT('1,2') y
from t2
 group by id )
 select id , x ,y from z ;

o/p 
----
id            x                                 y  
----     -------------                   ---------------
a C##SIVA.<b>VARCHAR_TT('1,2,3,4')</b>C##SIVA.VARCHAR_TT('1,2')
b C##SIVA.<b>VARCHAR_TT('1,2,3') </b>C##SIVA.VARCHAR_TT('1,2')
c C##SIVA.<b>VARCHAR_TT('1,2,4') </b>C##SIVA.VARCHAR_TT('1,2')
d C##SIVA.<b>VARCHAR_TT('1,2') </b>C##SIVA.VARCHAR_TT('1,2')


if i add below condition , i am not getting any result .

where y member of x ;

so i tried to convert to number array .

CREATE TYPE number_TT AS TABLE OF number;

with z as (            
 select id,number_TT(LISTAGG(val, ',') WITHIN GROUP (ORDER BY val)) as x ,number_TT(1,2) y
from t2
 group by id )
 select id , x ,y from z ;

ORA-01722: invalid number
01722. 00000 -  "invalid number"
*Cause:    The specified number was invalid.
*Action:   Specify a valid number.


1 ) Here i need o/p like below to use member and submultiset conditions.

o/p 
----
id            x                                 y  
----     -------------                   ---------------
a C##SIVA.<b>NUMBER_TT(1,2,3,4) </b>C##SIVA.NUMBER_TT(1,2)
b C##SIVA.<b>NUMBER_TT(1,2,3) </b>C##SIVA.NUMBER_TT(1,2)
c C##SIVA.<b>NUMBER_TT(1,2,4) </b>C##SIVA.NUMBER_TT(1,2)
d C##SIVA.<b>NUMBER_TT(1,2) </b>C##SIVA.NUMBER_TT(1,2)

 select varchar_tt('1,2') x ,number_TT(1,2) y  from dual;

       x                             y  
--------------------            ----------------
C##SIVA.VARCHAR_TT('1,2') C##SIVA.NUMBER_TT(1,2)


Please let me know how to convert character array to number array .

2)
create table t4 ( id VARCHAR2(1) , val number_tt )
 NESTED TABLE val STORE AS val_2 ;


How to insert into t4 table from t2 ?

expected o/p query of t4 table should be like below

id            val                          
----     -------------                   
a C##SIVA.<b>NUMBER_TT(1,2,3,4) </b>
b C##SIVA.<b>NUMBER_TT(1,2,3) </b>
c C##SIVA.<b>NUMBER_TT(1,2,4) </b>
d C##SIVA.<b>NUMBER_TT(1,2) </b>


i got similar output with below query .

select  id ,  CAST(MULTISET(SELECT val FROM t2 where t2.id=x.id) as number_tt) as val1  from t2 x

id       val1
---    --------------
a C##SIVA.NUMBER_TT(1, 2, 3, 4)
a C##SIVA.NUMBER_TT(1, 2, 3, 4)
a C##SIVA.NUMBER_TT(1, 2, 3, 4)
a C##SIVA.NUMBER_TT(1, 2, 3, 4)
b C##SIVA.NUMBER_TT(1, 2, 3)
b C##SIVA.NUMBER_TT(1, 2, 3)
b C##SIVA.NUMBER_TT(1, 2, 3)
c C##SIVA.NUMBER_TT(1, 2, 4)
c C##SIVA.NUMBER_TT(1, 2, 4)
c C##SIVA.NUMBER_TT(1, 2, 4)
d C##SIVA.NUMBER_TT(1, 2)
d C##SIVA.NUMBER_TT(1, 2)


But it has duplicates .

i tried with distinct but it is not working .

so i used group by , it is working .

select  id ,  CAST(MULTISET(SELECT val FROM t2 where t2.id=x.id) as number_tt) as val1  from t2 x
group by id 


Is there any way other than the above ?

and Chris said...

I think you're getting mixed up in how to find the matching rows. And how you display those rows in the end.

Listagg is handy for the final display. But it's not building an array! Just a string of comma separated values.

If you stick the output of listagg into a nested table, you have one element in the array. Which is a CSV string. To have each value as an array element, you need to collect() them in. Then do your comparisons on this.

You're dealing with numbers. So you want to compare nested tables of numbers. There are a couple of ways you can do this

Member

Use this to see if one value of the type for the nested table exists

with z as (            
  select id,
         cast ( collect ( val ) as number_TT ) n_array,
         3 n,
         number_TT ( 1, 3 ) ntt,
         listagg ( val, ',' ) within group ( order by val ) vals
  from   t2
  group  by id 
)
 select z.*
 from   z
 where  n member of n_array;

ID    N_ARRAY         N    NTT       VALS      
a     [1, 4, 3, 2]       3 [1, 3]    1,2,3,4    
b     [1, 3, 2]          3 [1, 3]    1,2,3  


Submutliset

Use this to check if all the elements in one nested table exist in another.

with z as (            
  select id,
         cast ( collect ( val ) as number_TT ) n_array,
         3 n,
         number_TT ( 1, 3 ) ntt,
         listagg ( val, ',' ) within group ( order by val ) vals
  from   t2
  group  by id 
)
 select z.*
 from   z
 where  ntt submultiset of n_array;

ID    N_ARRAY         N    NTT       VALS      
a     [1, 4, 3, 2]       3 [1, 3]    1,2,3,4    
b     [1, 3, 2]          3 [1, 3]    1,2,3  


Note - display of the nested tables in the results looking like arrays is SQL Developer's processing of this. Different clients will have different ways of showing these!

Of course, I'm assuming you need to use nested tables here for some reason. The problem here is a form of relational division. Which you can solve with pure SQL if you want to:

https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:9534489800346122396

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

A reader, June 17, 2019 - 4:38 am UTC

Hi Chris ,

Thanks for your clear explanation .
Chris Saxon
June 17, 2019 - 8:12 am UTC

You're welcome.

Add window functions ?

Jim, April 30, 2024 - 12:51 am UTC

Chris Do you have example that run in LiveSQL ?
For your code above, resultset includes "[unsupported data type]"

I want to add partition by / order by to n_array.
When I try I encounter an error (no detail available).
Not 100% sure if this is supported.

Chris Saxon
April 30, 2024 - 2:51 pm UTC

From 19c you can convert object types to/from JSON. So you can do something like this to convert the nested table to make it readable:

with z as (            
  select id,
         cast ( collect ( val ) as number_TT ) n_array,
         3 n,
         number_TT ( 1, 3 ) ntt,
         listagg ( val, ',' ) within group ( order by val ) vals
  from   t2
  group  by id 
)
 select json_array ( z.n_array )
 from   z
 where  n member of n_array;

JSON_ARRAY(Z.N_ARRAY)
[1,4,3,2]
[1,3,2]

Add window functions ?

Jim, May 06, 2024 - 8:00 am UTC

Chris - thanks for the json technique, it helps visual resultset
NB I still saw an error minus any useful details on livesql
So results below against autonomous free tier 19c

Struggling to add partition by / order by - or even verify whether this is legal syntax.
If possible, then I may need to add DISTINCT (actual use-case is complex - and may be suitable for 23ai graph, something I need to look into)

SQL> -- Oracle 19c
SQL> set echo on
SQL> set linesize 132
SQL> column id format a3
SQL> column n_array format A25
SQL> column ntt format A25
SQL> column vals format A25
SQL>
SQL> with z as (
2 select id,
3 cast ( collect ( val ) as number_TT ) n_array,
4 3 n,
5 number_TT ( 1, 3 ) ntt,
6 listagg ( val, ',' ) within group ( order by val ) vals
7 from t2
8 group by id
9 )
10 select z.id, json_array ( z.n_array ) as n_array, n, z.ntt, z.vals
11 from z
12 where z.n member of z.n_array ;

ID N_ARRAY N NTT VALS
--- ------------------------- ---------- ------------------------- -------------------------
a [1,4,3,2] 3 NUMBER_TT(1, 3) 1,2,3,4
b [1,3,2] 3 NUMBER_TT(1, 3) 1,2,3

SQL>
SQL> -- order by 1 - ok
SQL> with z as (
2 select --+ NO_PARALLEL
3 id,
4 cast ( collect ( val ) over (partition by id order by 1) as number_TT ) n_array,
5 3 n,
6 number_TT ( 1, 3 ) ntt,
7 listagg ( val, ',' ) within group ( order by val ) over (partition by id) vals
8 from t2
9 -- group by id
10 )
11 select --+ NO_PARALLEL
12 z.id, json_array ( z.n_array ) as n_array, n, z.ntt, z.vals
13 from z
14 where z.n member of z.n_array ;

ID N_ARRAY N NTT VALS
--- ------------------------- ---------- ------------------------- -------------------------
a [1,2,3,4] 3 NUMBER_TT(1, 3) 1,2,3,4
a [1,2,3,4] 3 NUMBER_TT(1, 3) 1,2,3,4
a [1,2,3,4] 3 NUMBER_TT(1, 3) 1,2,3,4
a [1,2,3,4] 3 NUMBER_TT(1, 3) 1,2,3,4
b [1,2,3] 3 NUMBER_TT(1, 3) 1,2,3
b [1,2,3] 3 NUMBER_TT(1, 3) 1,2,3
b [1,2,3] 3 NUMBER_TT(1, 3) 1,2,3

7 rows selected.

SQL>
SQL> -- add partition by & order by; remove group by
SQL> -- order by val : ORA-29400: data cartridge error & ORA-00602: internal programming exception: [PC:0x7FB4B26F83A6] [ADDR:0x000000000] ( & ORA-12801: error signaled in parallel query server P000, instance 2 )
SQL> with z as (
2 select --+ NO_PARALLEL
3 id,
4 cast ( collect ( val ) over (partition by id order by val) as number_TT ) n_array,
5 3 n,
6 number_TT ( 1, 3 ) ntt,
7 listagg ( val, ',' ) within group ( order by val ) over (partition by id) vals
8 from t2
9 -- group by id
10 )
11 select --+ NO_PARALLEL
12 z.id, json_array ( z.n_array ) as n_array, n, z.ntt, z.vals
13 from z
14 where z.n member of z.n_array ;

Error starting at line : 46 in command -
with z as (
select --+ NO_PARALLEL
id,
cast ( collect ( val ) over (partition by id order by val) as number_TT ) n_array,
3 n,
number_TT ( 1, 3 ) ntt,
listagg ( val, ',' ) within group ( order by val ) over (partition by id) vals
from t2
-- group by id
)
select --+ NO_PARALLEL
z.id, json_array ( z.n_array ) as n_array, n, z.ntt, z.vals
from z
where z.n member of z.n_array
Error report -
ORA-12801: error signaled in parallel query server P000, instance 2
ORA-29400: data cartridge error
ORA-00602: internal programming exception: [PC:0x7FB4B26F83A6] [ADDR:0x000000000]

SQL>
SQL> -- DISTINCT errors : ORA-00932: inconsistent datatypes: expected - got {schema}.NUMBER_TT
SQL> with z as (
2 select
3 id,
4 cast ( collect ( val ) over (partition by id order by 1) as number_TT ) n_array,
5 3 n,
6 number_TT ( 1, 3 ) ntt,
7 listagg ( val, ',' ) within group ( order by val ) over (partition by id) vals
8 from t2
9 -- group by id
10 )
11 select distinct
12 z.id, json_array ( z.n_array ) as n_array, n, z.ntt, z.vals
13 from z
14 where z.n member of z.n_array ;

Error starting at line : 62 in command -
with z as (
select
id,
cast ( collect ( val ) over (partition by id order by 1) as number_TT ) n_array,
3 n,
number_TT ( 1, 3 ) ntt,
listagg ( val, ',' ) within group ( order by val ) over (partition by id) vals
from t2
-- group by id
)
select distinct
z.id, json_array ( z.n_array ) as n_array, n, z.ntt, z.vals
from z
where z.n member of z.n_array
Error at Command Line : 73 Column : 55
Error report -
SQL Error: ORA-00932: inconsistent datatypes: expected - got {schema_name}.NUMBER_TT
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*Cause:
*Action:
SQL>
Chris Saxon
May 08, 2024 - 2:59 pm UTC

I'm unsure exactly what you're trying to do here - it's probably better to submit a new question with full details.

The error in 23ai gives a better clue as to the problem with the final statement:

with z as (
select id, cast ( collect ( val ) over (partition by id order by 1) as number_TT ) n_array
from t2
)
select distinct z.n_array
from z
/
ORA-22848: cannot use CHRIS.NUMBER_TT type as comparison key
An attempt was made to perform a comparison against a type without comparison support


The ORA-00602 error is a bug of some description - please raise this with support.


comparison of object type / collection

Jim, May 09, 2024 - 2:37 am UTC

Thanks Chris - will provide more details in new question

fundamentally I am trying to sort/collate/compare oracle object types / collections ( I think those are the correct terms )

Been working with Postgres and DuckDB recently and they both allow this comparison of arrays/lists.

I have may misunderstood Oracle but I was hoping to get either of the 2 commented out statements to run (and apply numeric collation on each element in n_array)

with z as ( select id, val, listagg ( val, ',' ) within group ( order by val ) over (partition by id) vals from t2 )
, y as ( select z.id, z.val, z.vals, apex_string.split_numbers(z.vals, ',') as n_array from z )
-- select * from y order by n_array ; -- ORA-00932: inconsistent datatypes: expected - got APEX_230200.WWV_FLOW_T_NUMBER
-- select distinct * from y ; -- ORA-00932: inconsistent datatypes: expected - got APEX_230200.WWV_FLOW_T_NUMBER
select * from y ;
Chris Saxon
May 09, 2024 - 10:19 am UTC

I'm still unclear what output you're expecting. Please submit a new question.

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.