I've created a function in a PL/SQL package that uses a custom type defined as table of numbers. I use an object of this type in an SQL query with the SELECT COLUMN_VALUE instruction like this :
The type definition in the package:
type T_IDS is table of my_table.col_id%type;
The query inside a procedure in the package body:
l_ids_list T_IDS ;
begin
select col_ids bulk collect into T_IDS from my_table;
select sum(t.rec_value) into total_value
from my_table t where t.col_id in (
select column_value from Table(l_ids_list) );
Everything works fine and when I compile this code, I can see a new type generated under my schema_name/type section.
Once I installed this on test environment, it fails the compilation with the errors :
Error: PLS-00642: local collection types not allowed in SQL statements
Error: PL/SQL: ORA-22905: cannot access rows from a non-nested table
item
Database versions (local and test) are exactly the same, 11g. Is there a way to activate such a generation on the DBMS?
exemple to reproduce :
create table my_table (
col_id number,
rec_value number
);
insert into my_table (col_id, rec_value) values (1,100);
insert into my_table (col_id, rec_value) values (2,200);
insert into my_table (col_id, rec_value) values (3,300);
insert into my_table (col_id, rec_value) values (4,400);
commit;
package creation :
create or replace package test_pck as
type T_IDS is table of my_table.col_id%type;
procedure test_list;
end test_pck;
/
create or replace
package body test_pck as
procedure test_list is
l_ids_list T_IDS ;
total_value number;
begin
select col_id bulk collect into l_ids_list from my_table;
select sum(t.rec_value) into total_value
from my_table t where t.col_id in (
select column_value from Table(l_ids_list) );
end test_list;
end test_pck;
/
what you are doing is wrong. you should be creating SQL types and not using pl/sql types to access the TABLE function.
now as to WHY it actually sorta worked in your Dev env.
The silent pl/sql table creation is intended for pipelined functions and has been around for a while, but you're using this in a non-pipelined function and as such THIS SHOULD FAIL. In 11g release 1 (11.1.0.7 to be precise) though, a bug means that it actually compiles. If you actually tried to run it though you would get an error:
SQL> create package body foo
2 as
3
4 procedure test
5 is
6 l_ids_list T_IDS ;
7 total_value number;
8 begin
9 select col_id bulk collect into l_ids_list from my_table;
10 select sum(t.rec_value) into total_value
11 from my_table t
12 where t.col_id in (select column_value from Table(l_ids_list));
13 end;
14 end;
15 /
Package body created.
SQL> exec foo.test;
BEGIN foo.test; END;
*
ERROR at line 1:
ORA-21700: object does not exist or is marked for delete
ORA-06512: at "TEST.FOO", line 10
ORA-06512: at line 1
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for Linux: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
now, Oracle FIXED this buggy behaviour in 11.2.0.3. now the error is thrown at compile time:
SQL> create package body foo
2 as
3
4 procedure test
5 is
6 l_ids_list T_IDS ;
7 total_value number;
8 begin
9 select col_id bulk collect into l_ids_list from my_table;
10 select sum(t.rec_value) into total_value
11 from my_table t
12 where t.col_id in (select column_value from Table(l_ids_list));
13 end;
14 end;
15 /
Warning: Package Body created with compilation errors.
SQL> show errors
Errors for PACKAGE BODY FOO:
LINE/COL ERROR
-------- -----------------------------------------------------------------
10/3 PL/SQL: SQL Statement ignored
12/48 PL/SQL: ORA-22905: cannot access rows from a non-nested table
item
12/54 PLS-00642: local collection types not allowed in SQL statements
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
in short, create an SQL type with create type and use that instead:
SQL> create type T_IDS as table of number;
2 /
Type created.
SQL> create package body foo
2 as
3
4 procedure test
5 is
6 l_ids_list T_IDS ;
7 total_value number;
8 begin
9 select col_id bulk collect into l_ids_list from my_table;
10 select sum(t.rec_value) into total_value
11 from my_table t
12 where t.col_id in (select column_value from Table(l_ids_list));
13 end;
14 end;
15 /
Package body created.
SQL> exec foo.test
PL/SQL procedure successfully completed.
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
Related
I am working in a games data base. I want to create a procedure which shows the games created between two dates.
I am using a cursor and a rowtype like this:
CREATE OR REPLACE procedure p_games(v_date1 games.date%type, v_date2 games.date%type)
AS
v_games games%rowtype;
CURSOR checkGames IS
SELECT * INTO v_games
FROM games
WHERE date BETWEEN v_date1 AND v_date2;
BEGIN
FOR register IN checkGames LOOP
dbms_output.put_line(register.v_games);
END LOOP;
END;
/
but when I run it the error is
PLS-00302: the component 'V_GAMES' must be declared.
Should I declare it in any other way?
Not exactly like that.
you don't have to declare cursor variable as you're using a cursor FOR loop
you don't select INTO while declaring a cursor; you would FETCH into if you used a different approach (see example below)
Sample table:
SQL> create table games
2 (id number,
3 c_date date
4 );
Table created.
SQL> insert into games (id, c_date) values (1, date '2022-04-25');
1 row created.
Your procedure, slightly modified:
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 BEGIN
9 FOR register IN checkGames LOOP
10 dbms_output.put_line(register.id);
11 END LOOP;
12 END;
13 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>
A different approach; as you can notice, a cursor FOR loop is way simpler as Oracle does most of the dirty job for you (opening the cursor, fetching from it, taking care about exiting the loop, closing the cursor):
SQL> CREATE OR REPLACE procedure p_games(v_date1 games.c_date%type, v_date2 games.c_date%type)
2 AS
3 CURSOR checkGames IS
4 SELECT *
5 FROM games
6 WHERE c_date BETWEEN v_date1 AND v_date2;
7
8 v_games checkGames%rowtype;
9 BEGIN
10 open checkGames;
11 loop
12 fetch checkGames into v_games;
13 exit when checkGames%notfound;
14
15 dbms_output.put_line(v_games.id);
16 END LOOP;
17 close checkGames;
18 END;
19 /
Procedure created.
SQL> set serveroutput on
SQL> exec p_games(date '2022-01-01', date '2022-12-31');
1
PL/SQL procedure successfully completed.
SQL>
I have gone through similar threads but unable to understand the root cause of the error.
I have an Oracle Schema as "PRP".
PRP has one table Named : "MY_TABLE".
PRP has one package Named : "My_PKG" with authid current_user
Package contains following procedure
PROCEDURE CUSTOMER_ORDER_QUERY (
P_REPORT OUT SYS_REFCURSOR
) AS
BEGIN
OPEN P_REPORT FOR SELECT
* from MY_TABLE
END;
When I execute the procedure from the package it gives the error Table or view does not exists.
But when I prefix the schema name to the table in the procedures the cursor executes perfectly.
I have explicitly given privileges on that table to same schema.
from sys users : grant all on prp.MY_TABLE to PRP;
But none helps.
The Package and the table is in same schema.
Please help.
I did what you described; no problems.
Create user prp and grant required privileges:
SQL> connect sys as sysdba
Enter password:
Connected.
SQL> create user prp identified by prp
2 default tablespace users
3 temporary tablespace temp
4 quota unlimited on users;
User created.
SQL> grant create session, create table, create procedure to prp;
Grant succeeded.
Connect as prp, create table and package:
SQL> connect prp/prp
Connected.
SQL> create table my_table as
2 select 1 id, 'Little' name from dual union all
3 select 2 , 'Foot' from dual;
Table created.
SQL> create or replace package my_pkg as
2 procedure customer_order_query (p_report out sys_refcursor);
3 end;
4 /
Package created.
SQL> create or replace package body my_pkg as
2 procedure customer_order_query (p_report out sys_refcursor)
3 as
4 begin
5 open p_report for select * from my_table;
6 end;
7 end;
8 /
Package body created.
Testing:
SQL> var l_rc refcursor
SQL> exec my_pkg.customer_order_query (:l_rc);
PL/SQL procedure successfully completed.
SQL> print l_rc;
ID NAME
---------- ------
1 Little
2 Foot
SQL>
So, yes - it works. If both table and package belong to the same user (reside in the same schema), you don't need any additional privileges as you own both of them.
I just bumped into some strange behaviour of DBMS_PARALLEL_EXECUTE (at least for me). See my preset (executed as SYS):
-- Preset
drop user usr1 cascade;
create user usr1 identified by usr1;
create or replace procedure usr1.do_stuff(p1 in number, p2 in number)
is
begin
dbms_output.put_line('I did stuff!');
end;
/
drop user usr2 cascade;
create user usr2 identified by usr2;
grant connect to usr2;
grant create job to usr2;
drop role stuff_doer cascade;
create role stuff_doer;
grant execute on usr1.do_stuff to stuff_doer;
grant stuff_doer to usr2;
So I created 2 users, the first one has a procedure which is given to stuff_doer role. Later this role is given to usr2.
Then I check it as usr2:
SQL*Plus: Release 11.2.0.4.0 Production on Fri May 22 12:14:10 2020
Copyright (c) 1982, 2013, Oracle. All rights reserved.
Enter user-name: usr2#db
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> set serveroutput on
SQL> set linesize 400
SQL> exec usr1.do_stuff(1,1);
I did stuff!
PL/SQL procedure successfully completed.
SQL> DECLARE
2 l_task_name VARCHAR2(100) := 'task_name';
3 l_splitter VARCHAR2(4000) := 'select 1, 1 from dual';
4 l_exec_stmt VARCHAR2(1000) := 'begin usr1.do_stuff(:start_id, :end_id); end;';
5 BEGIN
6 FOR line IN (SELECT d.task_name
7 FROM user_parallel_execute_tasks d
8 WHERE d.task_name = l_task_name)
9 LOOP
10 dbms_parallel_execute.drop_task(task_name => line.task_name);
11 END LOOP;
12
13 dbms_parallel_execute.create_task(l_task_name);
14 dbms_parallel_execute.create_chunks_by_sql(task_name => l_task_name
15 ,sql_stmt => l_splitter
16 ,by_rowid => FALSE);
17
18 dbms_parallel_execute.run_task(l_task_name
19 ,l_exec_stmt
20 ,dbms_sql.native);
21
22 COMMIT;
23
24 END;
25 /
PL/SQL procedure successfully completed.
SQL> column status format A20
SQL> select status from user_parallel_execute_tasks where task_name = 'task_name';
STATUS
--------------------
FINISHED_WITH_ERROR
SQL> column status format A20
SQL> column error_code format 900000
SQL> column error_message format A60
SQL> select status, ERROR_CODE, ERROR_MESSAGE from user_parallel_execute_chunks e where e.TASK_NAME = 'task_name';
STATUS ERROR_CODE ERROR_MESSAGE
-------------------- ---------- ------------------------------------------------------------
PROCESSED_WITH_ERROR -06550 ORA-06550: line 1, column 7:
PLS-00201: identifier 'USR1.DO_STUFF' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
SQL>
See: when I execute do_stuff procedure directly - it finishes as expected. But when I use DBMS_PARALLEL_EXECUTE I get identifier must be declared error. Am I missing something in granting privileges?
I found here this phrase: The CHUNK_BY_SQL, RUN_TASK, and RESUME_TASK subprograms require a query, and are executed using DBMS_SQL.
I tried to explicitly dbms_sql.parse my statement but it also finished OK.
Any help would be appreciated as I'm not getting current situation. And yes, I can grant privileges directly but still it's something tricky for me.
Roles are not activited by default in PL/SQL stored units (tested with Oracle 19 but it's the same behaviour in older releases since very long time):
SQL> set serveroutput on
SQL> select banner from v$version where rownum=1;
BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
SQL> show user;
USER is "USR2"
SQL> select * from session_roles;
ROLE
--------------------------------------------------------------------------------
CONNECT
SELECT_CATALOG_ROLE
HS_ADMIN_SELECT_ROLE
STUFF_DOER
SQL> --
SQL> begin
2 for r in (select role from session_roles)
3 loop
4 dbms_output.put_line('role=' || r.role);
5 end loop;
6 end;
7 /
role=CONNECT
role=SELECT_CATALOG_ROLE
role=HS_ADMIN_SELECT_ROLE
role=STUFF_DOER
PL/SQL procedure successfully completed.
SQL> show errors
No errors.
SQL> create or replace procedure sr is
2 begin
3 for r in (select role from session_roles)
4 loop
5 dbms_output.put_line('role=' || r.role);
6 end loop;
7 end;
8 /
Procedure created.
SQL> show errors
No errors.
SQL>
SQL> exec sr;
PL/SQL procedure successfully completed.
SQL>
Note the difference between anonymous PL/SQL (which is not stored in the database) and a stored unit (procedure/function stored in the database).
This is the right check is the USR2 can execute the procedure (requires CREATE PROCEDURE privilege)
create procedure stuff as
BEGIN
usr1.do_stuff;
END;
/
SQL> show errors
Errors for PROCEDURE STUFF:
LINE/COL ERROR
-------- -----------------------------------------------------------------
3/4 PL/SQL: Statement ignored
3/4 PLS-00201: identifier 'USR1.DO_STUFF' must be declared
I.e. the answer is no, direct execute privilege is required (not via a ROLE).
I tried creating following stored procedure found here using Execute Query client connected to my Oracle server (Oracle Database 11g Enterprise Edition Release 11.1.0.7.0)
CREATE PROCEDURE remove_emp (employee_id NUMBER) AS
tot_emps NUMBER;
BEGIN
DELETE FROM employees
WHERE employees.employee_id = remove_emp.employee_id;
tot_emps := tot_emps - 1;
END;
/
I'm getting following,
Procedure created.
17110 - Warning: execution completed with warning
Appreciate any help on this.
Always use SHOW ERRORS to see the complete compilation error stack.
I don't know what error you are getting, because it works for me:
SQL> CREATE PROCEDURE remove_emp (employee_id NUMBER) AS
2 tot_emps NUMBER;
3 BEGIN
4 DELETE FROM emp
5 WHERE emp.empno = remove_emp.employee_id;
6 tot_emps := tot_emps - 1;
7 END;
8 /
Procedure created.
SQL> sho err;
No errors.
SQL>
To execute:
SQL> exec remove_emp(9999);
PL/SQL procedure successfully completed.
SQL>
I'm attempting to create a procedure in Oracle Express Server (Application Express 2.1.0.00.39) using the web interface.
This is the SQL I'm running via the SQL Commands option in the web interface
CREATE OR REPLACE PROCEDURE my_procedure (listOfNumbers num_list,
v_value varchar2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE my_table
SET my_column = v_value
WHERE my_row_id IN (SELECT column_value
FROM TABLE(listOfNumbers));
COMMIT;
END;
UPDATE:
Changed SELECT column_value FROM TABLE to SELECT column_value FROM TABLE(listOfNumbers) but now I get the following error:
PLS-00201: identifier 'num_list' must
be declared
UPDATE 2:
Here is how I created my type:
CREATE OR REPLACE TYPE "num_list" as table of NUMBER(38,1)
/
Seems the error is being caused on the parameter declaration line:
(listOfNumbers num_list, v_value varchar2)
Below is the object details as displayed by the Oracle Database Express Edition web interface.
Try ...TABLE(CAST(listOfNumbers AS num_list)).
The SQL parser simply sees a bind placeholder in place of listOfNumbers, and since it's a custom type you need to tell it what type it is.
This will only work if num_list has been defined as a type in the schema, not just declared as a type in a PL/SQL block.
Your code works - providing the array type has been declared correctly (see below). As you are still having a problem I suspect that is where you are going wrong. But you need to post the code you are using to create the NUM_LIST type in order for us to correct it.
My test data:
SQL> select * from my_table
2 /
MY_COLUMN MY_ROW_ID
-------------------- ----------
APC 1
XYZ 2
JFK 3
SQL>
In order to use a type in a SQL statement we must create it as a SQL object:
SQL> create type num_list as table of number;
2 /
Type created.
SQL>
SQL>
SQL> create or replace procedure my_procedure
2 (listofnumbers num_list,
3 v_value varchar2)
4 is
5 begin
6
7 update my_table
8 set my_column = v_value
9 where my_row_id in (select column_value
10 from table(listofnumbers));
11
12 end;
13 /
Procedure created.
SQL>
Executing the procedure:
SQL> declare
2 n num_list := num_list(1,3);
3 begin
4 my_procedure (n , 'FOX IN SOCKS');
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
And lo!
SQL> select * from my_table
2 /
MY_COLUMN MY_ROW_ID
-------------------- ----------
FOX IN SOCKS 1
XYZ 2
FOX IN SOCKS 3
SQL>
Apparently I was creating the type with quotes around the name:
The following didn't work:
CREATE OR REPLACE TYPE "NUMBER_T" as table of NUMBER(38,1)
When I did it without the quotes and then created the procedure, it was able to recognize it.
The following did work:
CREATE OR REPLACE TYPE NUMBER_T as table of NUMBER(38,1)
I'm not sure why, but it worked.