Oracle error when selecting into temp table - oracle

My ultimate goal is far more complex than this, but this is the cut down version of what is causing my error. I want to put some rows into a temporary table (actually several temp tables, but I can't get by this first hurdle). Here is my PL/SQL;
DECLARE
type L1_store is table of MyTable%rowtype;
L1 L1_store;
BEGIN
select
* bulk collect
into L1
from MyTable
WHERE 1=1
and length(MyColumn1) = 2;
select
L1.MyColumn1
,L1.MyColumn2
from L1;
END;
And here is the error I get;
ORA-06550: line 19, column 6:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 16, column 1:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
The line numbers may be incorrect as I have edited the actual PL/SQL for clarity
****EDIT****
OK, so I originally accepted the answer offered below as it looks like it answers my question, and I can see how I could use it. However, for clarity, here is my ultimate goal, in case there is a better answer than the one I have in my head.
If I was just doing this in SQL I would do something like;
with L1 as
(select * from table),
L2 as
(select * from anothertable)
select L1.Column
from L1
left join L2 on L1.somecolumn = L2.somecolumn
I don't know if this helps or hinders, but thanks all in anticipation of your continued patience.

type L1_store is table of MyTable%rowtype; is not a temporary table; it is a collection data type declared in the PL/SQL scope and cannot be used in the SQL scope. Similarly, %ROWTYPE is a PL/SQL construct.
If you want to use a collection in SQL then declare it in the SQL scope:
CREATE TYPE mytable_data is OBJECT (
mycolumn1 VARCHAR2(50),
mycolumn2 NUMBER,
mycolumn3 DATE
);
CREATE TYPE mytable_data_table IS TABLE OF mytable_data;
so for some test data:
CREATE TABLE MyTable(
mycolumn1 VARCHAR2(50),
mycolumn2 NUMBER,
mycolumn3 DATE
);
INSERT INTO MyTable VALUES ( 'AA', 42, SYSDATE );
Then you can do:
DECLARE
L1 mytable_data_table;
BEGIN
select mytable_data( mycolumn1, mycolumn2, mycolumn3 )
bulk collect into L1
from MyTable
WHERE length(MyColumn1) = 2;
FOR i IN 1 .. L1.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( L1(i).mycolumn1 || ' ' || l1(i).mycolumn2 );
END LOOP;
END;
/
Which outputs:
AA 42
db<>fiddle here

The problem here seems to be the second select in the block. If you're trying to select the data from the record, I would be inclined to put it into a FOR loop. I.e, something like this:
for i in 1 .. L1.count() LOOP
dbms_output.put_line (L1(i).MyColumn1);
end loop;
You could assign the value of MYColumn1 to a variable or use it in some other way.

Related

Create Table in a Loop inside Stored Procedure Oracle SQL

I am attempting to create a Oracle stored procedure which creates partitioned tables based off of a table containing the table names and the column to be partitioned with. A separate PL/SQL block iterates through the table and calls the procedure with the table name and the column name.
Procedure:
create or replace PROCEDURE exec_multiple_table_create (
table_name IN VARCHAR2,
column_name IN VARCHAR2
) IS
stmt VARCHAR2(5000);
tablename VARCHAR2(50);
columnname VARCHAR2(50);
BEGIN
tablename := table_name;
columnname := column_name;
-- DBMS_OUTPUT.PUT_LINE(tablename);
-- DBMS_OUTPUT.PUT_LINE(columnname);
stmt := 'create table '
|| TABLENAME
|| '_temp as (select * from '
|| COLUMNNAME
|| ' where 1=2)';
EXECUTE IMMEDIATE stmt
USING IN table_name, column_name;
stmt := 'alter table '
|| tablename
|| '_temp modify partition by range('
|| columnname
|| ')
(PARTITION observations_past VALUES LESS THAN (TO_DATE(''20000101'',''YYYYMMDD'')),
PARTITION observations_CY_2000 VALUES LESS THAN (TO_DATE(''20010101'',''YYYYMMDD'')),
PARTITION observations_CY_2001 VALUES LESS THAN (TO_DATE(''20020101'',''YYYYMMDD'')),
PARTITION observations_CY_2002 VALUES LESS THAN (TO_DATE(''20030101'',''YYYYMMDD'')),
PARTITION observations_CY_2003 VALUES LESS THAN (TO_DATE(''20040101'',''YYYYMMDD'')),
PARTITION observations_CY_2004 VALUES LESS THAN (TO_DATE(''20050101'',''YYYYMMDD'')),
PARTITION observations_CY_2005 VALUES LESS THAN (TO_DATE(''20060101'',''YYYYMMDD'')),
PARTITION observations_CY_2006 VALUES LESS THAN (TO_DATE(''20070101'',''YYYYMMDD'')),
PARTITION observations_CY_2007 VALUES LESS THAN (TO_DATE(''20080101'',''YYYYMMDD'')),
PARTITION observations_CY_2008 VALUES LESS THAN (TO_DATE(''20090101'',''YYYYMMDD'')),
PARTITION observations_CY_2009 VALUES LESS THAN (TO_DATE(''20100101'',''YYYYMMDD'')),
PARTITION observations_CY_2010 VALUES LESS THAN (TO_DATE(''20110101'',''YYYYMMDD'')),
PARTITION observations_FUTURE VALUES LESS THAN ( MAXVALUE ) )';
EXECUTE IMMEDIATE stmt
USING IN table_name, column_name;
RETURN;
END exec_multiple_table_create;
The PL/SQL block which is using the stored proc is:
BEGIN
FOR partition_item IN (
SELECT
table_name,
partition_column
FROM
partition_table
) LOOP
exec_multiple_table_create(partition_item.table_name, partition_item.partition_column);
END LOOP;
END;
Now, when I try executing the thing, this is what I am seeing:
Error report -
ORA-06550: line 9, column 9:
PLS-00905: object SCG_MYACCT_CUSTOMPC.EXEC_MULTIPLE_TABLE_CREATE is invalid
ORA-06550: line 9, column 9:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have a feeling that I am missing something. Please let me know what it is. The table containing the reference data exists and contains data.
I have tried refreshing the table, rewriting and modifying the pl/sql block & the procedure code. Nothing seems to be working.
Thanks in advance.
UPDATE 1:
There was a glitch in the stored procedure where I needed to refer to the tablename rather than the columnname in the code above. However, I am getting a different error right now.
Error report -
ORA-06546: DDL statement is executed in an illegal context
ORA-06512: at "SCG_MYACCT_CUSTOMPC.EXEC_MULTIPLE_TABLE_CREATE", line 18
ORA-06512: at line 9
ORA-06512: at line 9
06546. 00000 - "DDL statement is executed in an illegal context"
*Cause: DDL statement is executed dynamically in illegal PL/SQL context.
- Dynamic OPEN cursor for a DDL in PL/SQL
- Bind variable's used in USING clause to EXECUTE IMMEDIATE a DDL
- Define variable's used in INTO clause to EXECUTE IMMEDIATE a DDL
*Action: Use EXECUTE IMMEDIATE without USING and INTO clauses to execute
the DDL statement.
Please help me out with this as well.
UPDATE 2:
I removed the USING part of the EXECUTE IMMEDIATE statement. That seemed to take care of the error I posted. Getting a different error with versions now:
Error starting at line : 1 in command -
BEGIN
FOR partition_item IN (
SELECT
table_name,
partition_column
FROM
partition_table
) LOOP
exec_multiple_table_create(partition_item.table_name, partition_item.partition_column);
END LOOP;
END;
Error report -
ORA-00406: COMPATIBLE parameter needs to be 12.2.0.0.0 or greater
ORA-00722: Feature "Conversion into partitioned table"
ORA-06512: at "SCG_MYACCT_CUSTOMPC.EXEC_MULTIPLE_TABLE_CREATE", line 37
ORA-06512: at line 9
ORA-06512: at line 9
00406. 00000 - "COMPATIBLE parameter needs to be %s or greater"
*Cause: The COMPATIBLE initialization parameter is not high
enough to allow the operation. Allowing the command would make
the database incompatible with the release specified by the
current COMPATIBLE parameter.
*Action: Shutdown and startup with a higher compatibility setting.

Trying to insert into a table while looping through pl SQL

I have a view that I need to loop through with all the inventory ids from another table. I keep receiving the following error: Error report -
ORA-06550: line 31, column 31:
PLS-00357: Table,View Or Sequence reference 'MISSINGINVENTORY' not allowed in this context
ORA-06550: line 31, column 2:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
This is my current code I am not sure what the problem would be? I ma fairly new to pl sql
DECLARE
CURSOR inventory_ids_c IS
SELECT DISTINCT fzrfbth_status status,
fzbfbth_inventory_id id
FROM fzbfbth#develop_sw,
fzrfbth#develop_sw
WHERE fzbfbth_inventory_id = fzrfbth_inventory_id
ORDER BY fzrfbth_status,
fzbfbth_inventory_id;
inventory_ids_rec inventory_ids_c%ROWTYPE;
BEGIN
OPEN inventory_ids_c;
FETCH inventory_ids_c INTO inventory_ids_rec;
LOOP
EXIT WHEN inventory_ids_c%NOTFOUND;
fzkfims.P_set_inventory_id#develop_sw(inventory_ids_rec.id);
DECLARE
CURSOR inventory_items IS
SELECT *
FROM fzvfims#develop_sw;
BEGIN
OPEN inventory_items;
LOOP
FETCH inventory_items INTO Missinginventory(last_inventory_date,
atype_title
,
owner, orgn_code, orgn_title, locn_code, room, bldg, sort_room,
ptag
,
manufacturer, model, serial_num, description, custodian, po,
acq_date,
amount,
ownership, schev_year, tag_type, inventory_id, condition,
asset_type
);
EXIT WHEN inventory_items%NOTFOUND;
END LOOP;
CLOSE inventory_items;
END;
END LOOP;
END;
It seems that you are trying to fetch from cursor directly into Missinginventory table (if it is a table). Well, that won't work. First fetch into cursor variables, then insert those variables into a table.
I don't know what this:
fzkfims.P_set_inventory_id#develop_sw(inventory_ids_rec.id);
does; looks like some procedure call. I guess it sets some kind of an "environment", but I don't know how it reflects to code you wrote.
Anyway: why do you use cursor loops? Can't you directly insert data into the table? That would be faster and - hopefully - simpler.
Also, if you declare something, do that at the same place, at the beginning of that PL/SQL procedure. If you scatter declarations all over your code, it is difficult to maintain it. Furthermore, see whether cursor FOR loops are an option as they are simpler to use - Oracle does a lot of things for you. How? Although you still have to write cursor's SELECT statement, you don't have to declare cursor variables, open cursor, fetch, take care about exiting the loop, close the cursor. I'll try to rewrite your code, have a look (as you can see, no declare section at all!).
begin
for cur_1 in (select distinct fzrfbth_status status,
fzbfbth_inventory_id id
from fzbfbth#develop_sw
join fzrfbth#develop_sw
on fzbfbth_inventory_id = fzrfbth_inventory_id
order by fzrfbth_status,
fzbfbth_inventory_id
)
loop
fzkfims.p_set_inventory_id#develop_sw(cur_1.id);
for cur_2 in (select *
from fzvfims#develop_sw
)
loop
-- I shortened it to just a few columns
insert into missinginventory
(last_inventory_date,
atype_title,
asset_type)
values (cur_2.last_inventory_date,
cur_2.atype_title,
cur_2.asset_type);
end loop;
end loop;
end;

ORA-22905 - While using DBMS_SQL.Number_Table for Table Operator

All,
Here is a simplified version of what I am attempting to accomplish. Instead of declaring my own type, I used the Number_Table type from the dbms_sql package.
First I created a simple test table:
CREATE TABLE collect_test(id NUMBER(38), other_info VARCHAR2(5));
Then, populated the table with a small amount of data:
INSERT INTO collect_test
SELECT rownum, chr(rownum+60)
FROM dual
CONNECT BY rownum <= 10;
Finally, I attempt to use PL/SQL to select some rows into a collection then use that collection to delete rows from the table:
DECLARE
l_tIDList DBMS_SQL.Number_Table;
BEGIN
SELECT ct.id
BULK COLLECT INTO l_tIDList
FROM collect_test ct
WHERE mod(ct.id, 2) = 0;
DELETE FROM (SELECT ct.id
FROM collect_test ct
INNER JOIN table(l_tIDList) ids ON ct.id = ids.column_value);
ROLLBACK;
END;
/
However, when I run this PL/SQL block I receive these errors:
ORA-06550: line 11, column 33:
PLS-00382: expression is of wrong type
ORA-06550: line 11, column 27:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
ORA-06550: line 9, column 3: PL/SQL: SQL Statement ignored
In all the other questions/articles I have found it seems like the coder was either trying to use a local type or forgetting to BULK COLLECT. I appreciate any suggestions you may have.
N.B.: I realize that I can do this specific functionality using a single DELETE statement. My actual scenario is more complicated and can't be done with a single statement.
Although it is not clear what you are trying to accomplish, there are two things which are wrong with your pl/sql block.
You can use the TABLE() function on the variables of collection
types defined in the schema and not on those defined locally
The way you are running your DML ( delete ) statement is wrong, it throws the error `
ORA-22841: DML on PL/SQL collections not supported.
So, create a collection TYPE in your schema and declare a collection of that type in your PL/SQL block and for your delete use an IN condition
CREATE OR REPLACE TYPE idlisttable as TABLE OF NUMBER;
DECLARE l_tidlist idlisttable;
BEGIN
SELECT ct.id BULK collect
INTO l_tidlist
FROM collect_test ct
WHERE MOD(ct.id, 2) = 0;
DELETE
FROM collect_test ct
WHERE ct.id IN (
SELECT ids.column_value
FROM TABLE (l_tidlist) ids
);
ROLLBACK;
END;
/
`
I have tried this using a cursor and it worked for me. Please try as below.
DECLARE
l_tIDList DBMS_SQL.Number_Table;
CURSOR c
IS
SELECT ct.id FROM collect_test ct WHERE mod(ct.id, 2) = 0;
BEGIN
OPEN c;
LOOP
FETCH c bulk collect INTO l_tIDList;
forall i IN 1 .. l_tIDList.count
DELETE FROM collect_test t WHERE t.id = l_tIDList(i);
EXIT
WHEN c%notfound;
END LOOP;
COMMIT;
CLOSE c;
END ;
/

PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got NUMBER

I'm executing PL/SQL code to display the Currency Code from the Failed Reservation table. Object type and Nested Table collections are used.
When the PL/SQL code is run, the following error is generated. The corresponding line is highlighted in the PL/SQL code section.
Error report:
ORA-06550: line 27, column 11:
PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got NUMBER
ORA-06550: line 27, column 4:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
The code is pasted below:
DDL - Table creation:
CREATE TABLE FAILEDRESERVATION
(
FAILEDRESERVATIONID NUMBER(18,0),
FK_TRANSACTIONID NUMBER(18,0),
DEBITRESERVATIONID NUMBER(18,0),
RESERVATIONTIME DATE,
RESERVATIONAMOUNT NUMBER(18,5),
CURRENCYCODE CHAR(3 BYTE),
AVAILABLEAMOUNT NUMBER(18,5)
);
ALTER TABLE FAILEDRESERVATION
ADD CONSTRAINT "PK_FAILEDRESERVATION" PRIMARY KEY ("FAILEDRESERVATIONID");
Object Type:
CREATE OR REPLACE TYPE TYPE type_failedreservation AS OBJECT
(
FK_TRANSACTIONID NUMBER(18),
DEBITRESERVATIONID NUMBER(18),
RESERVATIONTIME DATE,
RESERVATIONAMOUNT NUMBER(18,5),
CURRENCYCODE CHAR(3),
AVAILABLEAMOUNT NUMBER(18,5)
);
DML:
INSERT INTO FAILEDRESERVATION (FAILEDRESERVATIONID,FK_TRANSACTIONID,DEBITRESERVATIONID,RESERVATIONTIME,RESERVATIONAMOUNT,CURRENCYCODE,AVAILABLEAMOUNT)
VALUES (289,2,1,to_date('07-MAR-16','DD-MON-RR'),20000,'USD',10000);
INSERT INTO FAILEDRESERVATION (FAILEDRESERVATIONID,FK_TRANSACTIONID,DEBITRESERVATIONID,RESERVATIONTIME,RESERVATIONAMOUNT,CURRENCYCODE,AVAILABLEAMOUNT)
VALUES (288,1,1,to_date('01-MAR-16','DD-MON-RR'),10000,'NOK',10000);
Nested Tables:
CREATE OR REPLACE TYPE type_failedreservation_coll as TABLE OF type_failedreservation;
CREATE OR REPLACE TYPE type_dbtrsid_coll AS TABLE OF NUMBER;
PL/SQL Code:
DECLARE
P_FAILEDRESERVATION APPDATA.TYPE_FAILEDRESERVATION_COLL;
vdbtid_coll type_dbtrsid_coll := type_dbtrsid_coll();
BEGIN
SELECT TYPE_FAILEDRESERVATION(fk_transactionid,debitreservationid,reservationtime,reservationamount,currencycode,availableamount)
BULK COLLECT
INTO p_failedreservation
FROM failedreservation;
-- This is line 27
SELECT frs.debitreservationid
INTO vdbtid_coll
FROM TABLE(p_failedreservation) frs;
FOR v_iterate IN vdbtid_coll.FIRST..vdbtid_coll.LAST
LOOP
dbms_output.put_line('The currency code is: '||v_iterate);
END LOOP;
END;
Why is the code generating this error ?
You've declared vdbtid_coll as a collection type, so you need to bulk collect into that too:
SELECT frs.debitreservationid
BULK COLLECT INTO vdbtid_coll
FROM TABLE(p_failedreservation) frs;
With that change:
PL/SQL procedure successfully completed.
The currency code is: 1
The currency code is: 2
That's just giving you the index number in the collection though, so I don't think it's what you really want. You may want:
FOR v_iterate IN vdbtid_coll.FIRST..vdbtid_coll.LAST
LOOP
dbms_output.put_line('The currency code is: '
|| p_failedreservation(v_iterate).currencycode);
END LOOP;
which gets:
PL/SQL procedure successfully completed.
The currency code is: USD
The currency code is: NOK
You don't really need the second select/collection at all though, you can do:
FOR v_iterate IN 1..p_failedreservation.COUNT
LOOP
dbms_output.put_line('The currency code is: '
|| p_failedreservation(v_iterate).currencycode);
END LOOP;
... for the same result. Although I'm not sure what the relevance of the debitreservationid is in that second query, as it is the same value (1) in both rows.
You are trying to select multiple rows into a collection. You need to use BULK COLLECT INTO rather than just INTO.
Change
SELECT frs.debitreservationid
INTO vdbtid_coll
FROM TABLE(p_failedreservation) frs;
To
SELECT frs.debitreservationid
BULK COLLECT INTO vdbtid_coll
FROM TABLE(p_failedreservation) frs;
and you probably want the output to be:
FOR v_iterate IN vdbtid_coll.FIRST..vdbtid_coll.LAST LOOP
dbms_output.put_line('The currency code is: '|| vdbtid_coll(v_iterate) );
END LOOP;
However, you could simplify it all to:
DECLARE
P_FAILEDRESERVATION APPDATA.TYPE_FAILEDRESERVATION_COLL;
BEGIN
SELECT TYPE_FAILEDRESERVATION(
fk_transactionid,
debitreservationid,
reservationtime,
reservationamount,
currencycode,
availableamount
)
BULK COLLECT INTO p_failedreservation
FROM failedreservation;
FOR v_iterate IN p_failedreservation.FIRST .. p_failedreservation.LAST LOOP
dbms_output.put_line(
'The currency code is: '|| p_failedreservation(v_iterate).currencycode
);
END LOOP;
END;
Try casting the type array to its type as follows
SELECT frs.debitreservationid
BULK COLLECT INTO vdbtid_coll
FROM TABLE(CAST(p_failedreservation as APPDATA.TYPE_FAILEDRESERVATION_COLL)) frs;
PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got CHAR
You may get this error in case you write type table in place of type object when
using BULK COLLECT INTO, i faced this error coz i did so.
FOR EXAMPLE:
CREATE OR REPLACE TYPE OBJ_TYPE AS OBJECT (NAME VARCHAR2(20));
/
CREATE OR REPLACE TYPE TBL_TYPE IS TABLE OF OBJ_TYPE;
/
CREATE OR REPLACE FUNCTION FUNC1 RETURN TBL_TYPE AS
TBL_TEXT TBL_TYPE := TBL_TYPE();
BEGIN
SELECT ***OBJ_TYPE*** (NAME) BULK COLLECT INTO ***TBL_TEXT*** FROM <table-name> ;
RETURN ***TBL_TEXT***;
END;
/
Care should be taken when using object type and table type in the function.

How to locate the accurate postion in pl/sql promptly (ORA-06502: PL/SQL)

I have a select statement which needs to select dozens of column into self-defined variable in my pl/sql. Like as below:
select col1,
col2,
....
col30
into var1,
...
var30
from table
where ....
While executing the SP I encounter the error:
ORA-06502: PL/SQL: numeric or value error: character string buffer too
small
The error information only points out the first line number of select statement. Even if i can figure out that my defined variable is too small to hold the column, it still makes me hard to locate the error-defined variable precisely. This is not an efficient way for me to debug this sp.
Is there any better idea, please advise me.
Two options are typically used in pl/sql:
1.Define your variables in PL/SQL to match the table's definition, using %type.
define
v_col1 my_table.col1%type;
v_col2 my_table.col2%type;
begin
select col1,col2
into v_col1, v_col2
from my_table
-- some condition that pulls 1 row
where rownum = 1;
end;
2.Define a row variable, using %rowtype
define
v_my_table_row my_table%rowtype;
begin
select *
into v_my_table_row
from my_table
where rownum = 1;
end;

Resources