Oracle pl/sql problem when trying to reference plsql table in query - oracle

I am getting a pls-201 error when I try to reference a plsql table record in a query.
Here is an example of the issue:
DECLARE
TYPE trxtypeinforec IS RECORD(
NAME ra_cust_trx_types.NAME%TYPE
);
TYPE trxtypeinfotab IS TABLE OF trxtypeinforec
INDEX BY PLS_INTEGER;
g_inv_type trxtypeinfotab;
l_result VARCHAR2 (100);
BEGIN
g_inv_type(1).NAME := 'Test';
SELECT g_inv_type(qry.ID).NAME
INTO l_result
FROM (SELECT 1 ID
FROM dual) qry;
END;
Error report - ORA-06550: line 15, column 23: PLS-00201: identifier
'QRY.ID' must be declared ORA-06550: line 15, column 23: PLS-00201:
identifier 'QRY.ID' must be declared ORA-06550: line 15, column 12:
PL/SQL: ORA-00904: : invalid identifier ORA-06550: line 15, column 5:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:

Retrieve the index from the SELECT first into a local variable and then assign the value from the associative array to l_result in a PL/SQL scope and not in an SQL scope:
DECLARE
TYPE trxtypeinforec IS RECORD(
NAME ra_cust_trx_types.NAME%TYPE
);
TYPE trxtypeinfotab IS TABLE OF trxtypeinforec INDEX BY PLS_INTEGER;
g_inv_type trxtypeinfotab;
idx PLS_INTEGER;
l_result ra_cust_trx_types.NAME%TYPE;
BEGIN
g_inv_type(1).NAME := 'Test';
SELECT 1
INTO idx
FROM DUAL;
l_result := g_inv_type(idx).NAME;
DBMS_OUTPUT.PUT_LINE( l_result );
END;
/
Outputs:
Test
db<>fiddle here

Please try using a cursor.
SET SERVEROUTPUT ON;
DECLARE
TYPE trxtypeinforec IS RECORD(
NAME ra_cust_trx_types.NAME%TYPE
);
TYPE trxtypeinfotab IS TABLE OF trxtypeinforec
INDEX BY PLS_INTEGER;
g_inv_type trxtypeinfotab;
l_result VARCHAR2 (100);
-- Query for retrieving index goes in the cursor or use existing cursor
CURSOR cur_id IS
SELECT 1 AS ID FROM DUAL;
BEGIN
-- Populate records here or put inside loop as required
g_inv_type(1).NAME := 'Test';
-- Loop through the cursor to get the relevant index
FOR rec in cur_id
LOOP
SELECT g_inv_type(rec.ID).NAME
INTO l_result
FROM DUAL;
dbms_output.put_line (l_result);
END LOOP;
END;
/
It will be helpful if we could get some more information on how the existing cursor is fetching index values.
Output:
Test
PL/SQL procedure successfully completed.

Related

Converting SQL type to PLSQL collection / converting one collection type to another

Trying to convert SQL type collection to PLSQL type.
-- create an SQL type
create or replace type arrayforvarchar as table of varchar2(30);
/
Check below anonymous block:
declare
type arrayforvarcharplsql is table of varchar2(30);
var_plsql_array arrayforvarcharplsql;
var_sql_array arrayforvarchar := arrayforvarchar();
begin
select cola
bulk collect into var_plsql_array
FROM (
select 'X' as cola from dual
union all
select 'Y' as cola from dual
);
end;
/
How is it possible to assign values of var_plsql_array to var_sql_array apart from using loops?
Anyway simple assignment isn't working.
Not that I can find:
Using a simple assignment doesn't work as the arrays have different types:
declare
type arrayforvarcharplsql is table of varchar2(30);
var_plsql_array arrayforvarcharplsql;
var_sql_array arrayforvarchar;
begin
var_plsql_array := arrayforvarcharplsql( 'X', 'Y' );
var_sql_array := var_plsql_array;
end;
/
Outputs:
ORA-06550: line 8, column 22:
PLS-00382: expression is of wrong type
ORA-06550: line 8, column 5:
PL/SQL: Statement ignored
Trying to use CAST doesn't work in a PL/SQL scope as it only works in an SQL scope:
declare
type arrayforvarcharplsql is table of varchar2(30);
var_plsql_array arrayforvarcharplsql;
var_sql_array arrayforvarchar;
begin
var_plsql_array := arrayforvarcharplsql( 'X', 'Y' );
var_sql_array := CAST( var_plsql_array AS arrayforvarchar );
end;
/
Outputs:
ORA-06550: line 8, column 22:
PLS-00204: function or pseudo-column '' may be used inside a SQL statement only
ORA-06550: line 8, column 5:
PL/SQL: Statement ignored
Trying to pass the PL/SQL collection into an SQL scope to be able to use CAST doesn't work as PL/SQL collections can't be used in the SQL scope:
declare
type arrayforvarcharplsql is table of varchar2(30);
var_plsql_array arrayforvarcharplsql;
var_sql_array arrayforvarchar;
begin
var_plsql_array := arrayforvarcharplsql( 'X', 'Y' );
SELECT CAST( var_plsql_array AS arrayforvarchar )
INTO var_sql_array
FROM DUAL;
end;
/
Outputs:
ORA-06550: line 8, column 18:
PLS-00642: local collection types not allowed in SQL statements
Trying to pass the PL/SQL collection into a table collection expression in the SQL scope and using BULK COLLECT doesn't work for the same reason as the previous:
declare
type arrayforvarcharplsql is table of varchar2(30);
var_plsql_array arrayforvarcharplsql;
var_sql_array arrayforvarchar;
begin
var_plsql_array := arrayforvarcharplsql( 'X', 'Y' );
SELECT COLUMN_VALUE
BULK COLLECT INTO var_sql_array
FROM TABLE( var_plsql_array );
end;
/
Outputs:
ORA-06550: line 10, column 19:
PLS-00642: local collection types not allowed in SQL statements
ORA-06550: line 10, column 12:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
ORA-06550: line 8, column 5:
PL/SQL: SQL Statement ignored
However, using loops and assigning values individually does work:
declare
type arrayforvarcharplsql is table of varchar2(30);
var_plsql_array arrayforvarcharplsql;
var_sql_array arrayforvarchar;
begin
var_plsql_array := arrayforvarcharplsql( 'X', 'Y' );
var_sql_array := arrayforvarchar();
var_sql_array.EXTEND( var_plsql_array.COUNT );
FOR i IN 1 .. var_plsql_array.COUNT LOOP
var_sql_array(i) := var_plsql_array(i);
END LOOP;
end;
/
db<>fiddle here
No, you cannot assign the values of one collection type to another collection type without using the loops. See Assigning Values to Collection Variables:
Data Type Compatibility
You can assign a collection to a collection variable only if they have the same data type.
Having the same element type is not enough.
Even if either of these collections are declared in PL/SQL, it will not work:
declare
type t1 is table of int;
type t2 is table of int;
a t1 := t1 (1);
b t2;
begin b := a;
end;
/
ORA-06550: line 6, column 12:
PLS-00382: expression is of wrong type
There is no internal function like CAST to convert a collection of one type to a collection of another type. Although you can use your own function to hide the loop. Reproducible example:
create or replace type arrsql as table of varchar2 (30);
/
var rc refcursor
declare
type arrpls is table of varchar2(30);
source arrpls := arrpls ('aaa','bbb','ccc');
target arrsql;
function cast (s arrpls, astypeof arrsql) return arrsql is
t arrsql := arrsql ();
begin
t.extend (s.count);
for i in 1..s.count loop t(i) := s(i); end loop;
return t;
end;
begin
target := cast (source, astypeof=>target);
open :rc for select * from table (target);
end;
/
Result Sequence
------------------------------
aaa
bbb
ccc

Plsql Procedure in invalid state

I have created following procedure:
create or replace function getDepartmentById(name varchar2)
return number
is v_dep number(10)
begin
--the sql statement is totally fine
select deptno into v_dep from dept where dname = name;
end;
/
but when I call
select getDepartmentById('SALES') into dep from dual;
I get this error message
Error report -
ORA-06550: line 20, column 14:
PL/SQL: ORA-06575: Package or function GETDEPARTMENTBYID is in an invalid state
ORA-06550: line 20, column 7:
PL/SQL: SQL Statement ignored
You need to add a return value for the function. It goes like this :
CREATE OR replace FUNCTION Getdepartmentbyid(name VARCHAR2)
RETURN NUMBER
IS
v_dep NUMBER(10);
BEGIN
--the sql statement is totally fine
SELECT deptno
INTO v_dep
FROM dept
WHERE dname = name;
RETURN v_dep;
END;
You can not use a INTO in your select statement. You should call the function like this.
select getDepartmentById('SALES') from dual;

runtime plsql throwing table not exist error

I have below code which checks for table exists & proceed further.
Here problem is why its proceeding to else part even though table is not present in the database & also its returning "0"
SQL> SET serveroutput ON trimspool on feed off echo on
declare
c_cnt number;
t_cnt number;
begin
select count(1) into t_cnt from dba_tables where owner='PRODDBA' and table_name='IOT_LIST';
dbms_output.put_line(t_cnt);
if t_cnt = 0 then
dbms_output.put_line('NO_ACTION');
else
select count(1) into c_cnt from PRODDBA.IOT_LIST;
if c_cnt = 0 then
dbms_output.put_line('NO_ACTION');
else
for i in (select temp_table_name from PRODDBA.IOT_LIST)
loop
begin
dbms_output.put_line(i.temp_table_name);
execute immediate 'drop table PRODDBA.'||i.temp_table_name||' purge';
EXCEPTION WHEN OTHERS then
CONTINUE;
end;
end loop;
end if;
end if;
end;
/
select count(1) into c_cnt from PRODDBA.IOT_LIST;
ERROR at line 10:
ORA-06550: line 10, column 41:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 10, column 1:
PL/SQL: SQL Statement ignored
ORA-06550: line 14, column 47:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 14, column 11:
PL/SQL: SQL Statement ignored
ORA-06550: line 17, column 22:
PLS-00364: loop index variable 'I' use is invalid
ORA-06550: line 17, column 1:
PL/SQL: Statement ignored
ORA-06550: line 18, column 42:
PLS-00364: loop index variable 'I' use is invalid
ORA-06550: line 18, column 1:
PL/SQL: Statement ignored
You should make use of dynamic REFCURSOR to run a loop to drop tables dynamically.
SET SERVEROUTPUT ON
DECLARE
c_cnt NUMBER;
t_cnt NUMBER;
v_schema_name VARCHAR2(40) := 'PRODDBA';
v_table_name VARCHAR2(40) := 'IOT_LIST';
v_tabs VARCHAR2(40);
refcur SYS_REFCURSOR;
BEGIN
SELECT COUNT(1)
INTO t_cnt
FROM dba_tables
WHERE owner = v_schema_name
AND table_name =v_table_name;
dbms_output.put_line(t_cnt);
IF t_cnt = 0 THEN dbms_output.put_line('NO_ACTION');
ELSE
OPEN refcur FOR 'SELECT temp_table_name
FROM '||v_schema_name||'.'||v_table_name;
LOOP
BEGIN
FETCH refcur INTO v_tabs;
EXIT WHEN refcur%NOTFOUND;
EXECUTE IMMEDIATE 'drop table '||v_schema_name||'.'
|| v_tabs
|| ' purge';
dbms_output.put_line('DROPPED ' || v_tabs);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line( 'TABLE NOT FOUND: '||v_tabs);
CONTINUE;
END;
END LOOP;
IF refcur%ROWCOUNT = 0 THEN
dbms_output.put_line('NO_ACTION');
END IF;
END IF;
END;
/
Result
Before table creation
<execute the block>
0
NO_ACTION
PL/SQL procedure successfully completed.
After table creation
create table IOT_LIST ( temp_table_name varchar2(40));
INSERT INTO IOT_LIST values('T1');
INSERT INTO IOT_LIST values('T2');
INSERT INTO IOT_LIST values('T4');
<execute the block>
1
DROPPED T1
DROPPED T2
TABLE NOT FOUND: T4
PL/SQL procedure successfully completed.

Assigning more than one column to a collection using BULK COLLECT

I am not sure why I am getting this error with this code, Please help me with debugging this code Thanks in advance.
declare
type emp_t is table of employees%rowtype
index by pls_integer;
rec emp_t;
begin
select employee_id,salary,manager_id bulk collect into rec
from employees where rownum <100;
forall i in 1..rec.last
update employees
set salary=salary+10
where employee_id=rec(i).employee_id;
end;
ORA-06550: line 7, column 3:
PL/SQL: ORA-00913: too many values
ORA-06550: line 6, column 3:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have changed the code to the below format but it's still giving me "expression is of wrong type error"
declare
type emp_t is table of employees%rowtype
index by pls_integer;
rec emp_t;
begin
for val in (
select employee_id,salary,manager_id
from employees where rownum <100)
loop
rec:=val;
end loop;
forall i in 1..rec.last
update employees
set salary=salary+10
where employee_id=rec(i).employee_id;
end;
Use either one or the other:
TYPE emp_t IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
-- ^^^^^^^^^^^^^^^^^
-- each record is "one row" of table `employees`
...
SELECT * BULK COLLECT INTO rec FROM employees WHERE ROWNUM < 100;
-- ^
-- get all columns for each row
Or
TYPE emp_rec IS RECORD (
employee_id employees.employee_id%TYPE,
salary employees.salary%TYPE,
manager_id employees.manager_id%TYPE
);
TYPE emp_t IS TABLE OF emp_rec
-- ^^^^^^^
-- each record only contains the necessary fields
...
SELECT employee_id,salary,manager_id BULK COLLECT INTO rec FROM employees WHERE ROWNUM < 100;
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- get only what we need (and in that particular order)
Very probably you are looking for the second form. Using a custom type for the RECORD you are trying to bulk collect. Please notice the use of %TYPE attribute in the record declaration. That's an alias to the type of each column.
emp_t is declared as ROWTYPE, that includes all the columns,
but while retrieving records your are retrieving only employee_id,salary,manager_id,
that could be the reason of your error.
Try this :
declare
type emp_t is table of employees%rowtype
index by pls_integer;
rec emp_t;
begin
select employee_id,salary,manager_id bulk collect into rec
from employees where rownum <100;
forall i in 1..rec.last
update employees
set salary=salary+10
where employee_id=rec(i).employee_id;
end;
The second one seem to have other issues like TYPE difference VAR and REC

'[Error] Execution (8: 3): ORA-06502: PL/SQL: numeric or value error: character string buffer too small ORA-06512: at line 5' in the following plsql

thanks for all,i had modified from varchar2(10) to varchar2(20) and removing the column keyword in execute immediate statement then program is executing
declare
coldate varchar2(20);
colname varchar2(20);
begin
coldate :='varchar2(10)';
colname :='smaple';
execute immediate 'alter table smap1 add column '||colname ||' '||coldate ;
end;
if i want to take the values dynamically i had used the following code
declare
coldate varchar2(20):=&coldate;
colname varchar2(20):=&colname;
begin
execute immediate 'alter table smap1 add '||colname ||' '||coldate ;
end;
then i am getting errors
[Error] Execution (11: 23): ORA-06550: line 2, column 23:
PLS-00330: invalid use of type name or subtype name
ORA-06550: line 2, column 9:
PL/SQL: Item ignored
ORA-06550: line 3, column 23:
PLS-00201: identifier 'SMAPLE' must be declared
ORA-06550: line 3, column 9:
PL/SQL: Item ignored
ORA-06550: line 6, column 45:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 6, column 1:
PL/SQL: Statement ignored
declare
coldate varchar2(20);
colname varchar2(20);
begin
coldate :='varchar2(10)';
colname :='smaple';
execute immediate 'alter table smap1 add column '||colname ||' '||coldate ;
end;
This query wont work as syntactically its not correct.
Please try this as it will help you definitely
declare
coldate varchar2(20):='varchar2(20)';
colname varchar2(20):='Newadd';
begin
execute immediate 'alter table <tablename> add '||colname ||' '||coldate ;
end;
-- And to use dynamically input values Please try this
SET DEFINE ON;
declare
coldate varchar2(20):='&DATATYPE';
colname varchar2(20):='&COL_NAME';
begin
execute immediate 'alter table emp add '||colname ||' '||coldate ;
end;
The below will solve it:
coldate varchar2(12);
colname varchar2(20);
begin
coldate :='varchar2(10)';

Resources