Alternate of using NVL with IN clause in Oracle 11g - sql

I have a workplace problem to which I am looking for an easy solution.
I am trying to replicate it in a smaller scenario.
Problem in short
I want to use nvl inside an in clause. Currently I have an input string which consists of a name. It is used in a where clause like below
and column_n = nvl(in_parameter,column_n)
Now I want to pass multiple comma separated values in same input parameter. So if I replace = with in, and transpose the input comma separated string as rows, I cannot use the nvl clause with it.
Problem in Detail
Lets consider an Employee table emp1.
Emp1
+-------+-------+
| empno | ename |
+-------+-------+
| 7839 | KING |
| 7698 | BLAKE |
| 7782 | CLARK |
+-------+-------+
Now this is a simple version of an existing stored procedure
create or replace procedure emp_poc(in_names IN varchar2)
as
cnt integer;
begin
select count(*)
into cnt
from emp1
where
ename = nvl(in_names,ename); --This is one of the condition where we will make the change.
dbms_output.put_line(cnt);
end;
So this will give the count of number of employees passed as Input Parameter. But if we pass null, it will return the whole count of employee becuase of the nvl.
So these procedure calls will render the given outputs.
Procedure Call Output
exec emp_poc('KING') 1
exec emp_poc('JOHN') 0
exec emp_poc(null) 3
Now what I want to achieve is to add another functionality. So exec emp_poc('KING,BLAKE') should give me 2. So I figured a way to split the comma separated string to rows and used that in the procedure.
So if I change the where clause as below to in
create or replace procedure emp_poc2(in_names IN varchar2)
as
cnt integer;
begin
select count(*)
into cnt
from emp1
where
ename in (select trim(regexp_substr(in_names, '[^,]+', 1, level))
from dual
connect by instr(in_names, ',', 1, level - 1) > 0
);
dbms_output.put_line(cnt);
end;
So exec emp_poc2('KING','BLAKE') gives me 2. But passing null will give result as 0. However I want to get 3 like the case with emp_proc
And I cannot use nvl with in as it expect the subquery to return a single value.
1 way I can think of is rebuilding the whole query in a variable, based in input paramteter, and then use execute immediate. But I am using some variables to collect the value and it would be difficult to achieve it with execute immediate.
I am again emphasizing that this is a simple version of a complex procedure where we are capturing many variables and it joins many tables and has multiple AND conditions in where clause.
Any ideas on how to make this work.

This may help you
CREATE OR REPLACE PROCEDURE emp_poc2(in_names IN varchar2)
AS
cnt integer;
BEGIN
SELECT COUNT(*) INTO cnt
FROM emp1
WHERE
in_names IS NULL
OR ename IN (
SELECT TRIM(REGEXP_SUBSTR(in_names, '[^,]+', 1, level))
FROM dual
CONNECT BY INSTR(in_names, ',', 1, level - 1) > 0
);
dbms_output.put_line(cnt);
END;
Other way could be use IF ELSE or UNION ALL

If your real code is much more complex then your code readability might be greatly enhanced by using a proper collection type instead.
In the example below I have created an user defined type str_list_t that is a real collection of strings.
I also use common table expression (CTE) in the sql query to enhance the readability. In this simple example the CTE benefits for readability are not obvious but for all non-trivial queries it's a valuable tool.
Test data
create table emp1(empno number, empname varchar2(10));
insert into emp1 values(5437, 'GATES');
insert into emp1 values(5438, 'JOBS');
insert into emp1 values(5439, 'BEZOS');
insert into emp1 values(5440, 'MUSK');
insert into emp1 values(5441, 'CUBAN');
insert into emp1 values(5442, 'HERJAVEC');
commit;
Supporting data type
create or replace type str_list_t is table of varchar2(4000 byte);
/
Subprogram
create or replace function emp_count(p_emps in str_list_t) return number is
v_count number;
v_is_null_container constant number :=
case
when p_emps is null then 1
else 0
end;
begin
-- you can also test for empty collection (that's different thing than a null collection)
with
-- common table expression (CTE) gives you no benefit in this simple example
emps(empname) as (
select * from table(p_emps)
)
select count(*)
into v_count
from emp1
where v_is_null_container = 1
or empname in (select empname from emps)
;
return v_count;
end;
/
show errors
Example run
SQL> select 2 as expected, emp_count(str_list_t('BALLMER', 'CUBAN', 'JOBS')) as emp_count from dual
union all
select 0, emp_count(str_list_t()) from dual
union all
select 6, emp_count(null) from dual
;
EXPECTED EMP_COUNT
---------- ----------
2 2
0 0
6 6

Related

Can views be created in PL/SQL Functions?

I'm at the beginning of learning PL/SQL. My question is can views be created inside a function?
CREATE OR REPLACE FUNCTION most_sold_item(in_year INT)
RETURN INT
IS
most_shipped_item INT := 0;
BEGIN
CREATE OR REPLACE VIEW Shipped_Items AS
SELECT ITEM_ID,SUM(QUANTITY) AS Total_Quantity
FROM ORDERS, ORDER_ITEMS
WHERE Orders.Order_ID =Order_Items.Order_ID
AND Status=1
AND Order_Year=in_year
GROUP BY Item_ID;
SELECT Item_ID
INTO most_shipped_item
FROM Shipped_Items
WHERE Total_quantity=(
SELECT MAX(Total_Quantity)
FROM Shipped_Items);
return most_shipped_item;
END;
/
Here's my code. There seems to be some kind of error which I just can't find. Can anyone please help me with this?
Thank you.
Here's the snippet of the datatbase that I'm working on
Here's a rough idea of what you might do, if you wish to use a function for this (Added logic to break ties):
-- Estimated structure based on posted image:
CREATE TABLE orders (
order_id int primary key
, client_id int
, order_year int
, status int
);
CREATE TABLE order_items (
order_id int
, item_id int
, quantity int
, ppu int
, primary key (order_id, item_id)
);
-- The adjusted function:
CREATE OR REPLACE FUNCTION most_sold_item(in_year INT)
RETURN INT
IS
most_shipped_item INT := 0;
BEGIN
WITH shipped_items AS (
SELECT ITEM_ID, SUM(QUANTITY) AS Total_Quantity
FROM ORDERS, ORDER_ITEMS
WHERE Orders.Order_ID =Order_Items.Order_ID
AND Status=1
AND Order_Year=in_year
GROUP BY Item_ID
)
SELECT Item_ID
INTO most_shipped_item
FROM Shipped_Items
WHERE Total_quantity=(
SELECT MAX(Total_Quantity)
FROM Shipped_Items
)
ORDER BY item_id
FETCH FIRST 1 ROW ONLY
;
return most_shipped_item;
END;
/
Don't forget to protect for the case where more than one item_id matches the MAX. That's easy to do, but I don't know how you wish to handle that case. I've adjusted the above solution to protect against this issue.
The question edit queue is full. Here's an executable test case (without data) that could be added to the question:
Executable test case, derived from the posted image (without data)
To answer your "original" question:
Can views be created in PL/SQL Functions?
Yes, they can. Should you do it? Definitely not, that's not how Oracle works (in most cases). In Oracle, we create objects at SQL level and use them in SQL or PL/SQL, but we do not create them from PL/SQL.
Anyway, here you are; read comments within code. I used Scott's sample schema as I don't have your tables, but the principle remains the same.
Function:
SQL> create or replace function f_test(par_deptno in number)
2 return number
3 is
4 -- you can't perform DDL in PL/SQL procedures unless they are
5 -- autonomous transactions
6 pragma autonomous_transaction;
7 retval number;
8 begin
9 -- you can't perform DML in a function unless you use dynamic SQL
10 execute immediate
11 'create or replace view v_emp as ' ||
12 ' select ename, sal ' ||
13 ' from emp ' ||
14 ' where deptno = ' || dbms_assert.enquote_literal(par_deptno);
15
16 -- you can't use an ordinary SELECT statement because at time of
17 -- procedure compilation view V_EMP doesn't exist yet, so compile
18 -- would fail
19 execute immediate
20 'select sum(sal) from v_emp ' into retval;
21
22 return retval;
23 end;
24 /
Function created.
Testing:
SQL> select f_test (20) from dual;
F_TEST(20)
----------
10875
What does the view contain?
SQL> select * from v_emp;
ENAME SAL
---------- ----------
SMITH 800
JONES 2975
SCOTT 3000
ADAMS 1100
FORD 3000
SQL>

Using SEQUENCE(Oracle) in WHERE Clause [duplicate]

The following Oracle SQL code generates the error "ORA-02287: sequence number not allowed here":
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA');
SELECT * FROM Customer where CustomerID=Customer_Seq.currval;
The error occurs on the second line (SELECT statement). I don't really understand the problem, because this does work:
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA');
SELECT Customer_Seq.currval from dual;
You have posted some sample code, so it is not clear what you are trying to achieve. If you want to know the assigned value, say for passing to some other procedure you could do something like this:
SQL> var dno number
SQL> insert into dept (deptno, dname, loc)
2 values (deptno_seq.nextval, 'IT', 'LONDON')
3 returning deptno into :dno
4 /
1 row created.
SQL> select * from dept
2 where deptno = :dno
3 /
DEPTNO DNAME LOC
---------- -------------- -------------
55 IT LONDON
SQL>
Edit
We can use the RETURNING clause to get the values of any column, including those which have been set with default values or by trigger code.
You don't say what version of Oracle you are using. There have in the past been limitations on where sequences can be used in PL/SQL - mostly if not all gone in 11G. Also, there are restrictions in SQL - see this list.
In this case you may need to write:
SELECT Customer_Seq.currval INTO v_id FROM DUAL;
SELECT * FROM Customer where CustomerID=v_id;
(Edited after comments).
This doesn't really directly answer your question, but maybe what you want to do can be resolved using a the INSERT's RETURNING clause?
DECLARE
-- ...
last_rowid rowid;
-- ...
BEGIN
-- ...
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA') RETURNING rowid INTO last_rowid;
SELECT * FROM Customer where rowid = last_rowid;
-- ...
END;
/
You may not use a sequence in a WHERE clause - it does look natural in your context, but Oracle does not allow the reference in a comparison expression.
[Edit]
This would be a PL/SQL implementation:
declare
v_custID number;
cursor custCur is
select customerid, name from customer
where customerid = v_custID;
begin
select customer_seq.nextval into v_custID from dual;
insert into customer (customerid, name) values (v_custID, 'AAA');
commit;
for custRow in custCur loop
dbms_output.put_line(custRow.customerID||' '|| custRow.name);
end loop;
end;
You have not created any
sequence
First create any sequence its cycle and cache. This is some basic example
Create Sequence seqtest1
Start With 0 -- This Is Hirarchy Starts With 0
Increment by 1 --Increments by 1
Minvalue 0 --With Minimum value 0
Maxvalue 5 --Maximum Value 5. So The Cycle Of Creation Is Between 0-5
Nocycle -- No Cycle Means After 0-5 the Insertion Stopes
Nocache --The cache Option Specifies How Many Sequence Values Will Be Stored In Memory For Faster Access
You cannot do Where Clause on Sequence in SQL beacuse you cannot filter a sequence . Use procedures like #APC said

How to declare variable and use it in the same Oracle SQL script?

I want to write reusable code and need to declare some variables at the beginning and reuse them in the script, such as:
DEFINE stupidvar = 'stupidvarcontent';
SELECT stupiddata
FROM stupidtable
WHERE stupidcolumn = &stupidvar;
How can I declare a variable and reuse it in statements that follow such as in using it SQLDeveloper.
Attempts
Use a DECLARE section and insert the following SELECT statement in BEGIN and END;. Acces the variable using &stupidvar.
Use the keyword DEFINE and access the variable.
Using the keyword VARIABLE and access the the variable.
But I am getting all kinds of errors during my tries (Unbound variable, Syntax error, Expected SELECT INTO...).
There are a several ways of declaring variables in SQL*Plus scripts.
The first is to use VAR, to declare a bind variable. The mechanism for assigning values to a VAR is with an EXEC call:
SQL> var name varchar2(20)
SQL> exec :name := 'SALES'
PL/SQL procedure successfully completed.
SQL> select * from dept
2 where dname = :name
3 /
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
SQL>
A VAR is particularly useful when we want to call a stored procedure which has OUT parameters or a function.
Alternatively we can use substitution variables. These are good for interactive mode:
SQL> accept p_dno prompt "Please enter Department number: " default 10
Please enter Department number: 20
SQL> select ename, sal
2 from emp
3 where deptno = &p_dno
4 /
old 3: where deptno = &p_dno
new 3: where deptno = 20
ENAME SAL
---------- ----------
CLARKE 800
ROBERTSON 2975
RIGBY 3000
KULASH 1100
GASPAROTTO 3000
SQL>
When we're writing a script which calls other scripts it can be useful to DEFine the variables upfront. This snippet runs without prompting me to enter a value:
SQL> def p_dno = 40
SQL> select ename, sal
2 from emp
3 where deptno = &p_dno
4 /
old 3: where deptno = &p_dno
new 3: where deptno = 40
no rows selected
SQL>
Finally there's the anonymous PL/SQL block. As you see, we can still assign values to declared variables interactively:
SQL> set serveroutput on size unlimited
SQL> declare
2 n pls_integer;
3 l_sal number := 3500;
4 l_dno number := &dno;
5 begin
6 select count(*)
7 into n
8 from emp
9 where sal > l_sal
10 and deptno = l_dno;
11 dbms_output.put_line('top earners = '||to_char(n));
12 end;
13 /
Enter value for dno: 10
old 4: l_dno number := &dno;
new 4: l_dno number := 10;
top earners = 1
PL/SQL procedure successfully completed.
SQL>
Try using double quotes if it's a char variable:
DEFINE stupidvar = "'stupidvarcontent'";
or
DEFINE stupidvar = 'stupidvarcontent';
SELECT stupiddata
FROM stupidtable
WHERE stupidcolumn = '&stupidvar'
upd:
SQL*Plus: Release 10.2.0.1.0 - Production on Wed Aug 25 17:13:26 2010
Copyright (c) 1982, 2005, Oracle. All rights reserved.
SQL> conn od/od#etalon
Connected.
SQL> define var = "'FL-208'";
SQL> select code from product where code = &var;
old 1: select code from product where code = &var
new 1: select code from product where code = 'FL-208'
CODE
---------------
FL-208
SQL> define var = 'FL-208';
SQL> select code from product where code = &var;
old 1: select code from product where code = &var
new 1: select code from product where code = FL-208
select code from product where code = FL-208
*
ERROR at line 1:
ORA-06553: PLS-221: 'FL' is not a procedure or is undefined
In PL/SQL v.10
keyword declare is used to declare variable
DECLARE stupidvar varchar(20);
to assign a value you can set it when you declare
DECLARE stupidvar varchar(20) := '12345678';
or to select something into that variable you use INTO statement, however you need to wrap statement in BEGIN and END, also you need to make sure that only single value is returned, and don't forget semicolons.
so the full statement would come out following:
DECLARE stupidvar varchar(20);
BEGIN
SELECT stupid into stupidvar FROM stupiddata CC
WHERE stupidid = 2;
END;
Your variable is only usable within BEGIN and END so if you want to use more than one you will have to do multiple BEGIN END wrappings
DECLARE stupidvar varchar(20);
BEGIN
SELECT stupid into stupidvar FROM stupiddata CC
WHERE stupidid = 2;
DECLARE evenmorestupidvar varchar(20);
BEGIN
SELECT evenmorestupid into evenmorestupidvar FROM evenmorestupiddata CCC
WHERE evenmorestupidid = 42;
INSERT INTO newstupiddata (newstupidcolumn, newevenmorestupidstupidcolumn)
SELECT stupidvar, evenmorestupidvar
FROM dual
END;
END;
Hope this saves you some time
If you want to declare date and then use it in SQL Developer.
DEFINE PROPp_START_DT = TO_DATE('01-SEP-1999')
SELECT *
FROM proposal
WHERE prop_start_dt = &PROPp_START_DT
The question is about to use a variable in a script means to me it will be used in SQL*Plus.
The problem is you missed the quotes and Oracle can not parse the value to number.
SQL> DEFINE num = 2018
SQL> SELECT &num AS your_num FROM dual;
old 1: SELECT &num AS your_num FROM dual
new 1: SELECT 2018 AS your_num FROM dual
YOUR_NUM
----------
2018
Elapsed: 00:00:00.01
This sample is works fine because of automatic type conversion (or whatever it is called).
If you check by typing DEFINE in SQL*Plus, it will shows that num variable is CHAR.
SQL>define
DEFINE NUM = "2018" (CHAR)
It is not a problem in this case, because Oracle can deal with parsing string to number if it would be a valid number.
When the string can not parse to number, than Oracle can not deal with it.
SQL> DEFINE num = 'Doh'
SQL> SELECT &num AS your_num FROM dual;
old 1: SELECT &num AS your_num FROM dual
new 1: SELECT Doh AS your_num FROM dual
SELECT Doh AS your_num FROM dual
*
ERROR at line 1:
ORA-00904: "DOH": invalid identifier
With a quote, so do not force Oracle to parse to number, will be fine:
17:31:00 SQL> SELECT '&num' AS your_num FROM dual;
old 1: SELECT '&num' AS your_num FROM dual
new 1: SELECT 'Doh' AS your_num FROM dual
YOU
---
Doh
So, to answer the original question, it should be do like this sample:
SQL> DEFINE stupidvar = 'X'
SQL>
SQL> SELECT 'print stupidvar:' || '&stupidvar'
2 FROM dual
3 WHERE dummy = '&stupidvar';
old 1: SELECT 'print stupidvar:' || '&stupidvar'
new 1: SELECT 'print stupidvar:' || 'X'
old 3: WHERE dummy = '&stupidvar'
new 3: WHERE dummy = 'X'
'PRINTSTUPIDVAR:'
-----------------
print stupidvar:X
Elapsed: 00:00:00.00
There is an other way to store variable in SQL*Plus by using Query Column Value.
The COL[UMN] has new_value option to store value from query by field name.
SQL> COLUMN stupid_column_name new_value stupid_var noprint
SQL> SELECT dummy || '.log' AS stupid_column_name
2 FROM dual;
Elapsed: 00:00:00.00
SQL> SPOOL &stupid_var.
SQL> SELECT '&stupid_var' FROM DUAL;
old 1: SELECT '&stupid_var' FROM DUAL
new 1: SELECT 'X.log' FROM DUAL
X.LOG
-----
X.log
Elapsed: 00:00:00.00
SQL>SPOOL OFF;
As you can see, X.log value was set into the stupid_var variable, so we can find a X.log file in the current directory has some log in it.
Just want to add Matas' answer.
Maybe it's obvious, but I've searched for a long time to figure out that the variable is accessible only inside the BEGIN-END construction, so if you need to use it in some code later, you need to put this code inside the BEGIN-END block.
Note that these blocks can be nested:
DECLARE x NUMBER;
BEGIN
SELECT PK INTO x FROM table1 WHERE col1 = 'test';
DECLARE y NUMBER;
BEGIN
SELECT PK INTO y FROM table2 WHERE col2 = x;
INSERT INTO table2 (col1, col2)
SELECT y,'text'
FROM dual
WHERE exists(SELECT * FROM table2);
COMMIT;
END;
END;
In Toad I use this works:
declare
num number;
begin
---- use 'select into' works
--select 123 into num from dual;
---- also can use :=
num := 123;
dbms_output.Put_line(num);
end;
Then the value will be print to DBMS Output Window.
Reference to here and here2.
Here's your answer:
DEFINE num := 1; -- The semi-colon is needed for default values.
SELECT &num FROM dual;
You can use a with clause and move filter criteria from a where to a join.
It helps here: Oracle SQL alternative to using DEFINE.
with
mytab as (select 'stupidvarcontent' as myvar from dual)
SELECT
stupiddata
FROM
stupidtable a
inner join
mytab b
on
a.stupidcolumn = b.myvar
WHERE ...;
It works in Oracle 12R2.
It works for one SQL command only.
It is standard ANSI notation.
I'm using it in SQL Developer.
One possible approach, if you just need to specify a parameter once and replicate it in several places, is to do something like this:
SELECT
str_size /* my variable usage */
, LPAD(TRUNC(DBMS_RANDOM.VALUE * POWER(10, str_size)), str_size, '0') rand
FROM
dual /* or any other table, or mixed of joined tables */
CROSS JOIN (SELECT 8 str_size FROM dual); /* my variable declaration */
This code generates a string of 8 random digits.
Notice that I create a kind of alias named str_size that holds the constant 8. It is cross-joined to be used more than once in the query.
Sometimes you need to use a macro variable without asking the user to enter a value. Most often this has to be done with optional script parameters. The following code is fully functional
column 1 noprint new_value 1
select '' "1" from dual where 2!=2;
select nvl('&&1', 'VAH') "1" from dual;
column 1 clear
define 1
Similar code was somehow found in the rdbms/sql directory.

oracle call stored procedure inside select

I'm working on a query (a SELECT) and I need to insert the result of this one in a table.
Before doing the insert I have some checking to do, and if all columns are valid, I will do the insert.
The checking is done in a stored procedure. The same procedure is used somewhere else too.
So I'm thinking using the same procedure to do my checks.
The procedure does the checkings and insert the values is all OK.
I tryied to call the procedure inside my SELECT but it does not works.
SELECT field1, field2, myproc(field1, field2)
from MYTABLE.
This kind of code does not works.
I think it can be done using a cursor, but I would like to avoid the cursors.
I'm looking for the easiest solution.
Anybody, any idea ?
use a PL/SQL loop:
BEGIN
FOR c IN (SELECT field1, field2 FROM mytable) LOOP
my_proc(c.field1, c.field2);
END LOOP;
END;
SQL can only use functions in the projection: it needs something which returns a value. So you are going to have to write some functions. That's the bad news. The good news is, you can re-use all the investement in your stored procedures.
Here is a procedure which enforces a completely just business rule: only managers can have a high salary.
SQL> create or replace procedure salary_rule
2 ( p_sal in emp.sal%type
3 , p_job in emp.job%type)
4 is
5 x_sal exception;
6 begin
7 if p_sal > 4999 and p_job != 'MANAGER' then
8 raise x_sal;
9 end if;
10 exception
11 when x_sal then
12 raise_application_error(-20000, 'Only managers can earn that much!');
13 end salary_rule;
14 /
Procedure created.
SQL>
Because it is a procedure we cannot use it in a SELECT statement; we need to wrap it in a function. This function just calls the stored procedure. It returns the input parameter P_SAL. In other words, if the salary is valid (according to the rules) it will be returned. Otherwise the function will re-hurl the stored procedure's exception.
SQL> create or replace function validate_salary
2 ( p_sal in emp.sal%type
3 , p_job in emp.job%type)
4 return emp.sal%type
5 is
6 begin
7 salary_rule(p_sal, p_job);
8 return p_sal;
9 end validate_salary;
10 /
Function created.
SQL>
The function has to return a value which we want to insert into our table. It cannot return some meaningless phrase like "salary okay". Also, if we want to validate two columns we need a separate function for each, even if there is a relationship between them and we use the same stored procedure to validate them both. Good use for the DETERMINISTIC keyword.
Here's the test: plumbers cannot earn 5000 spondulicks ....
SQL> insert into emp
2 (empno
3 , ename
4 , job
5 , deptno
6 , sal )
7 select
8 emp_seq.nextval
9 , 'HALL'
10 , 'PLUMBER'
11 , 60
12 , validate_salary(5000, 'PLUMBER')
13 from dual
14 /
, validate_salary(5000, 'PLUMBER')
*
ERROR at line 12:
ORA-20000: Only managers can earn that much!
ORA-06512: at "APC.SALARY_RULE", line 12
ORA-06512: at "APC.VALIDATE_SALARY", line 7
SQL>
... but managers can (because they deserve it):
SQL> insert into emp
2 (empno
3 , ename
4 , job
5 , deptno
6 , sal )
7 select
8 emp_seq.nextval
9 , 'HALL'
10 , 'MANAGER'
11 , 60
12 , validate_salary(5000, 'MANAGER')
13 from dual
14 /
1 row created.
SQL>
Note that the hurled exception is crucial to this working. We cannot write some bizarre IF SALARY IS VALID THEN INSERT logic in our SQL statement. So, if the stored procedure doesn't raise an exception but instead returns some wimpy error status the wrapping function will have to interpret the output and hurl its own exception.
You can't use stored procedures in SELECT statement.
You can use functions for that.
As I understand you are calling insert in your SP, so take into consideration that you can's use INSERT/UPDATE in function body. But if you need to do some checks you can use function which will do that checks and use that function in your select statement.

Arrays in Oracle SQL

Here's a simplified pseudo-code version of what I'd like to be able to do in PL-SQL (Oracle):
DECLARE
mylist as ARRAY
BEGIN
mylist (1) := '1'
mylist (2) := '3'
...
SELECT *
FROM aTable
WHERE aKey IN mylist;
END;
The SELECT should return the matching records for mylist(1), mylist(2) etc. It should be similar to ORing all the values, but of course we don't know in advance how many values we get.
How can I achieve this? I know that PL/SQL has some collection datatypes, but I can't seem to get them to work properly in SQL statements.
Thanks for any ideas.
This is easy to do with the TABLE() function. The one catch is that the array variable must use a type declared in SQL. This is because SELECT uses the SQL engine, so PL/SQL declarations are out of scope.
SQL> create or replace type numbers_nt as table of number
2 /
Type created.
SQL>
SQL> declare
2 l_array numbers_nt;
3 begin
4 l_array := numbers_nt (7521,7566,7654);
5 for r in ( select ename
6 from emp
7 where empno in ( select *
8 from table (l_array)
9 )
10 )
11 loop
12 dbms_output.put_line ( 'employee name = '||r.ename);
13 end loop;
14 end;
15 /
employee name = PADFIELD
employee name = ROBERTSON
employee name = BILLINGTON
PL/SQL procedure successfully completed.
SQL>
A couple of suggestions:
1.) There's a CAST SQL keyword that you can do that might do the job... it makes your collection be treated as if it were a table.
2.) Pipelined functions. Basically a function returns data that looks like a table.
This link summarises the options and has a number of code listings that explain them.
http://www.databasejournal.com/features/oracle/article.php/3352091/CASTing-About-For-a-Solution-Using-CAST-and-Table-Functions-in-PLSQL.htm