1

I have a nested table that includes a list of emails, which could be inside several times. The problem is I need to get the distinct list of items, and the number of times it appears on the list.

|    emails    |
| ------------ |
| a@mail.com   |
| b@mail.com   |
| c@mail.com   |
| d@mail.com   |
| c@mail.com   |
| c@mail.com   |
| a@mail.com   |
| a@mail.com   |
| b@mail.com   |
| b@mail.com   |
| c@mail.com   |

Ideally, my result would be a table or an output that tells me the following:

| Email | Number |
| ---------- | - |
| a@mail.com | 3 |
| b@mail.com | 3 |
| c@mail.com | 4 |
| d@mail.com | 1 |

To select from a table I would use a select statement, but if I try this in my code I get an error "ORA- 00942: table or view does not exist" same with even a simple select from emails table so I'm just guessing you can't use select on nested tables that way.

The nested table was created like this:

type t_email_type is table of varchar2(100);
  t_emails t_email_type := t_email_type();

and then populated under a loop that adds an email for each iteration of the loop:

t_emails.extend;
t_emails(t_emails.LAST) := user_r.email;
MT0
  • 143,790
  • 11
  • 59
  • 117
Jafex
  • 11
  • 2
  • Sorry about the lack of code on the answer, I'm having trouble with an error saying i'm not formatting it correctly and the toolbar does nothing but add ticks around the text I enter. – Jafex Nov 02 '22 at 14:50

2 Answers2

0

I tried to do what you described so far; here you go:

Table that contains e-mail addresses (some of them are duplicates):

SQL> select * from test_mails;

EMAIL
----------
a@mail.com
b@mail.com
c@mail.com
a@mail.com
b@mail.com

Type you created; I think you used type within your PL/SQL procedure. That won't work if it is a function which is supposed to return result of that type because it then must be created at SQL level, so - that's what I'm doing:

SQL> create or replace type t_email_type is table of varchar2(100);
  2  /

Type created.

Function: FOR loop selects e-mail addresses from the table and puts them into t_emails. What you're interested in is what follows in lines #12-14 as it shows how to return the result:

SQL> create or replace function f_test
  2    return t_email_type
  3  is
  4    t_emails t_email_type := t_email_type();
  5    retval t_email_type;
  6  begin
  7    for user_r in (select email from test_mails) loop
  8      t_emails.extend;
  9      t_emails(t_emails.last) := user_r.email;
 10    end loop;
 11
 12    select distinct column_value
 13      bulk collect into retval
 14      from table(t_emails);
 15    return retval;
 16  end;
 17  /

Function created.

OK, let's test it:

SQL> select * from table(f_test);

COLUMN_VALUE
--------------------------------------------------------------------------------
a@mail.com
b@mail.com
c@mail.com

SQL>

Distinct addresses; that's what you asked for.

Littlefoot
  • 131,892
  • 15
  • 35
  • 57
0

Try it like this:

SET SERVEROUTPUT ON
Create or replace type t_email_type is table of varchar2(100);
/
Declare     --  Outer block - just to populate variable emails of type t_email_type
    Cursor c_emails IS          
        Select 'a@mail.com' "EMAIL" From Dual Union All
        Select 'b@mail.com' "EMAIL" From Dual Union All
        Select 'c@mail.com' "EMAIL" From Dual Union All
        Select 'd@mail.com' "EMAIL" From Dual Union All
        Select 'c@mail.com' "EMAIL" From Dual Union All
        Select 'c@mail.com' "EMAIL" From Dual Union All
        Select 'a@mail.com' "EMAIL" From Dual Union All
        Select 'a@mail.com' "EMAIL" From Dual Union All
        Select 'b@mail.com' "EMAIL" From Dual Union All
        Select 'b@mail.com' "EMAIL" From Dual Union All
        Select 'c@mail.com' "EMAIL" From Dual;
    email     VarChar2(100);
    emails    t_email_type := t_email_type();
    i         Number(3) := 0;
Begin
    OPEN c_emails;
    LOOP
        FETCH c_emails Into email;
        EXIT WHEN c_emails%NOTFOUND;
        i := i + 1;
        emails.extend;
        emails(i) := email;
    END LOOP;
    CLOSE c_emails;
    --  Inner block - get distinct emails and number of appearances from variable emails of type t_email_type
    Declare
        Cursor c Is
            Select COLUMN_VALUE "EMAIL", Count(*) "NUM_OF_APPEARANCES"
            From    TABLE(emails)
            Group By COLUMN_VALUE
            Order By COLUMN_VALUE;
            cSet    c%ROWTYPE;
            i       Number(3) := 0;
    Begin
        OPEN c;
        LOOP
            FETCH c Into cSet;
            EXIT WHEN c%NOTFOUND;
            i := i + 1;
            If i = 1 Then
                DBMS_OUTPUT.PUT_LINE(RPAD('EMAIL', 20, ' ') || ' ' || LPAD('NUM_OF_APPEARANCES', 20, ' '));
                DBMS_OUTPUT.PUT_LINE(RPAD('-', 20, '-') || ' ' || LPAD('-', 20, '-'));
            End If;
            DBMS_OUTPUT.PUT_LINE(RPAD(cSet.EMAIL, 20, ' ') || ' ' || LPAD(cSet.NUM_OF_APPEARANCES, 20, ' '));
        END LOOP;
        CLOSE c;
    End;
End;
/*  R e s u l t :
anonymous block completed
EMAIL                  NUM_OF_APPEARANCES
-------------------- --------------------
a@mail.com                              3
b@mail.com                              3
c@mail.com                              4
d@mail.com                              1
*/

Outer block of this code is here just to generate data and to insert it into emails variable that is of type you have defined (t_email_type). You, probably, need just the inner block to get you list of emails with number of appearances within the table type.

SQL that gives you expected result is, actualy, the cursor in the inner block:

Select COLUMN_VALUE "EMAIL", Count(*) "NUM_OF_APPEARANCES"
From    TABLE(emails)
Group By COLUMN_VALUE
Order By COLUMN_VALUE;

Result:

EMAIL NUM_OF_APPEARANCES
a@mail.com 3
b@mail.com 3
c@mail.com 4
d@mail.com 1

Regards...

d r
  • 3,848
  • 2
  • 4
  • 15