1

I have a great problem with an PL-SQL package I'm currently working with.

All I want to do is to create a small piece of code which will do this:

In the IS section of a function:

l_tabellen_excl DBMS_utility.name_array;

Later in the code:

SELECT DISTINCT TABLE_NAME
BULK COLLECT INTO l_tabellen_excl
FROM
ALL_TAB_COLUMNS
WHERE
TABLE_NAME IN ('TAB_1', 'TAB_2');

To finally use this variable in a SELECT statement:

AND col.table_name NOT IN (SELECT * FROM TABLE (l_tabellen_excl))

I get ORA-22905: cannot access rows from a non-nested table item here.

I know that I could just write AND col.table_name NOT IN ('TAB_1','TAB_2') but I don't want to hardcode it in the deepest pits of the code... making it hard to find and less configurable.

I tried a TON of things:

type array_t is table of varchar2(10);

Doesn't work. I get an error saying that I can't use a locally declared collection in an SELECT statement.

I tried to cast the variable i_tabellen_excl on a locally declared type - like a workaround. But I get ORA-00902 - invalid datatype.

I Tried to declare a VARCHAR2 containing a string with comma separated table list, it seems to be working but still it is far away from a clean, good written, well designed code.

I tried other options not worth mentioning, like trying to write a function etc.

I'm lost in this matter, ANY ideas would be great to test out.

Lalit Kumar B
  • 47,486
  • 13
  • 97
  • 124
hc0re
  • 1,806
  • 2
  • 26
  • 61

1 Answers1

1

If you want to use the TABLE function, then you must create the TYPE as an OBJECT type which will store it in the database and create a function using it. Later you could use the table function in your PL/SQL code.

Otherwise, the way you are doing, using a NESTED TABLE you will have to LOOP again to reference the collection object.

SET serveroutput ON
DECLARE
type str_typ
IS
  TABLE OF VARCHAR2(200);
  str_sub str_typ := str_typ ();
BEGIN
  SELECT DISTINCT TABLE_NAME BULK COLLECT INTO str_sub FROM ALL_TAB_COLUMNS;
  FOR i IN 1..str_sub.count
  LOOP
    dbms_output.put_line(str_sub(i));
  END LOOP;
END;
/

So, you cannot use it as a database object, for that you must create the type as an object type, and create a table function.

For example,

Create the type

SQL> CREATE TYPE col_type AS OBJECT (
  2    col_name VARCHAR2(50)
  3  );
  4  /

Type created.

SQL> CREATE TYPE col_sub_type IS TABLE OF col_type;
  2  /

Type created.

Build the table function

SQL> CREATE OR REPLACE FUNCTION get_col_tab RETURN col_sub_type AS
  2    l_type  col_sub_type := col_sub_type();
  3  BEGIN
  4    FOR i IN (SELECT DISTINCT TABLE_NAME t_name FROM ALL_TAB_COLUMNS) LOOP
  5      l_type.extend;
  6      l_type(l_type.last) := col_type(i.t_name);
  7    END LOOP;
  8
  9    RETURN l_type;
 10  END;
 11  /

Function created.

Test it

SELECT *
FROM   TABLE(get_col_tab());
Lalit Kumar B
  • 47,486
  • 13
  • 97
  • 124
  • I've put it in the Oracle Package, but still - a hell lot of compilation errors. And still - do I can access this str_sub in a loop? All I need is to declare a variable containing selected table names and use them in a select statement to exclude them from the output :( – hc0re Oct 12 '15 at 09:07
  • I see the edited version. Might be much more useful now! Thanks! – hc0re Oct 12 '15 at 09:09
  • @dziki For the first approach, using nested table, you need to form a dynamic sql and use EXECUTE IMMEDIATE. Becuase you have to loop through the table names and use it dynamically. – Lalit Kumar B Oct 12 '15 at 09:16
  • one more question. Where in the Package body should I put it? Whatever I try - doesnt work. – hc0re Oct 12 '15 at 09:20
  • What are you trying? The first way using nested table or the table function? For the first way, you just need to do it in your package body. Just declare the nested table and initialize it. You can see the working example. Plain and simple PL/SQL. For the second approach, using table function. you need to do it outside one time. The types and function will be created as database objects and will be stored in database. You don't have to create any type/collection in the package, all you need to do is refer the table function directly just like any other static table. – Lalit Kumar B Oct 12 '15 at 09:25
  • The second approach is working fine, but It's less configurable. I just don't get the NESTED TABLE approach. I want to use the output in a SQL statement... Which I cannot do here, right? – hc0re Oct 12 '15 at 10:02
  • Even the NESTED table approach is possible, however, you need to use dynamic sql for that. So that, in the loop you could pass the table name and use it in the filter. Are you comfortable with `dynamic sql` and `execute immediate`? – Lalit Kumar B Oct 12 '15 at 10:04
  • May be if you tell me your exact requirement I can help you better, i.e. why do you want to put the metadata information in a collection? What are you trying to achieve from the code? – Lalit Kumar B Oct 12 '15 at 10:06
  • Well, it's not that second approach is not configurable. You can have a look at **RECORD** type in PL/SQL. – Lalit Kumar B Oct 12 '15 at 10:07
  • I want to declare a: String, array or table which will contain names of the tables to exclude. Secondly, I want to use this array/string/whatever in a SELECT statement, in example: let's say that the variable is called `tables`. Under this variable of a chosen type there are hidden the names of the tables that I want to exclude. Then in the SQL Select statement I want to use something like this: `AND TABLE_NAME NOT LIKE tables`. This AND statement is hidden around 240 line of the Oracle Package, I want to have the `tables` variable near the beginning of the file... – hc0re Oct 12 '15 at 10:13
  • Then why not create a parameterize cursor, and pass the table names to exclude as varying IN list. Not the best solution, but you do have 3 choices. See my blog on varying IN list http://lalitkumarb.com/2015/01/02/varying-in-list-of-values-in-where-clause/ and see this article for parameterized cusror http://www.java2s.com/Tutorial/Oracle/0500__Cursor/AnexampleofparameterizedcursorusingcursorFORLOOP.htm. I personally would use the second approach of table function, and make it pipelined function. – Lalit Kumar B Oct 12 '15 at 10:25