I'm giving a small snippet of my code but I 'm receiving the below error when trying to create a new order for a stored Oracle pl sql procedure.
line 83 is the insert statement in the code and line 84 is in the insert part of the statement.
83/5 PL/SQL: SQL Statement ignored
84/47 PL/SQL: ORA-00984: column not allowed here
BEGIN
--Initializing values for variables
x_rowcount := 0;
x_stockonhand := 0;
Totaldue := 0;
--Total due calculation
--(price of phone*quantity + shipping cost)*1.06 (assuming 6% sales tax)
Totaldue := (((i_price * c_p_qty) + i_shipping_cost) * 1.06);
SAVEPOINT start_transaction; -- mark a savepoint
--INSERT a new record into order table.
INSERT INTO orders(o_id,c_id,p_id,s_id,order_date,o_qty,order_total,card_type,cc_number,exp_date,shipping_status)
VALUES (orders_seq.nextval, c_c_id,c_p_id,s_id,sysdate,c_p_qty,Totaldue,c_card_type,c_cc_number,c_exp_date,'Not shipped yet');
Check your declaration section. Usually this error appears when you make a typo in variable name or variable not declared. For example:
SQL> create table tmp (id number, str varchar2(100));
Table created.
SQL> declare
a number;
begin
insert into tmp (id, str)
values (a, a1);
end;
/
values (a, a1);
*
ERROR at line 5:
ORA-06550: line 5, column 14:
PL/SQL: ORA-00984: column not allowed here
ORA-06550: line 4, column 3:
PL/SQL: SQL Statement ignored
The error is because one or more of the values in your VALUES(...,...) section is invalid.
I would suggest checking each one to see that they are valid. For example, is c_c_id declared and given a value somewhere else in the code? If not, this is likely your problem. Each one needs to be declared and given a value before it can be put in the VALUES(...,...) section of the INSERT statement.
INSERT INTO orders(o_id,c_id,p_id,s_id,order_date,o_qty,order_total,card_type,cc_number,exp_date,shipping_status)
VALUES (orders_seq.nextval, c_c_id,c_p_id,s_id,sysdate,c_p_qty,Totaldue,c_card_type,c_cc_number,c_exp_date,'Not shipped yet');
Related
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.
I am trying to write a code for every stock value that is $75 or more
add a "*" in the STK_FLAG column. my error repots are: Error report -
ORA-06550: line 15, column 21: PLS-00201: identifier 'STK_FLG' must be
declared ORA-06550: line 15, column 5: PL/SQL: SQL Statement ignored
ORA-06550: line 23, column 7: PL/SQL: ORA-00904: "STK_FLG": invalid
identifier ORA-06550: line 17, column 5: PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Blockquote
SET SERVEROUTPUT ON
DECLARE
CURSOR CURR
IS
SELECT STK_FLAG
FROM MM_MOVIE
WHERE MOVIE_VALUE * MOVIE_QTY >= 75
FOR UPDATE;
BEGIN
OPEN CURR;
LOOP
FETCH CURR INTO STK_FLG;
UPDATE
MM_MOVIE
SET
STK_FLG= '*'
WHERE
CURRENT OF CURR;
EXIT
WHEN CURR%notfound;
END LOOP;
Commit;
CLOSE CURR;
END;
/
You didn't declare a cursor variable (so there's nowhere you can put values returned by the cursor). Don't name it as column name; use a prefix, such as v_ or l_ or whichever you want.
Furthermore, in UPDATE you referenced a non-existent column. Cursor suggests its name is stk_flag, not stk_flg
Therefore, code that might be OK is
DECLARE
CURSOR curr IS
SELECT stk_flag
FROM mm_movie
WHERE movie_value * movie_qty >= 75
FOR UPDATE;
l_stk_flag mm_movie.stk_flag%TYPE; --> this
BEGIN
OPEN curr;
LOOP
FETCH curr INTO l_stk_flag;
EXIT WHEN curr%NOTFOUND;
UPDATE mm_movie
SET stk_flag = '*' --> this
WHERE CURRENT OF curr;
END LOOP;
COMMIT;
CLOSE curr;
END;
/
Why use a pl/sql anonymous block? Even if there is an "external" requirement for the functionality wrapped into pl/sql why use a cursor and loop? Using code that might be OK (from #Littlefoot) you retrieve a single column meeting your condition, iterate the resulting record set fetching that column but otherwise do nothing with it, and update a single row on every iteration of the loop with a literal value. SQL is designed for processing entire sets of rows at a time. Your processing can be done in a single update statement. Assuming there is an external requirement for a pl/sql block your code reduces to:
BEGIN
UPDATE mm_movie
SET stk_flag = '*'
WHERE movie_value * movie_qty >= 75;
COMMIT;
END;
Take away: When working with SQL stop thinking in terms of iterating (loops). Instead think of the commonality (set) of all objects to be processed. A different way of looking at problems and their corresponding solutions to be sure. Getting used to thinking that way will take some time, but in the long run your SQL and procedures will greatly improve because of it. Both in performance and clarity.
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.
Given a PL/SQL block where I have access to 2 recordtype variables, I want to insert these 2 records into the same table in a single statement, but my attempts to use INSERT ALL have failed thus far. Is it possible to use INSERT ALL with record variables at all?
Here is some code that works, using dedicated inserts:
DECLARE
mProperty1 MyTable%ROWTYPE;
mProperty2 MyTable%ROWTYPE;
...
BEGIN
...
INSERT INTO MyTable VALUES mProperty1;
INSERT INTO MyTable VALUES mProperty2;
...
END;
If I try to convert the statement to a INSERT ALL though, it fails with an error message:
DECLARE
mProperty1 MyTable%ROWTYPE;
mProperty2 MyTable%ROWTYPE;
...
BEGIN
...
INSERT ALL
INTO MyTable VALUES mProperty1
INTO MyTable VALUES mProperty2
SELECT 1 FROM DUAL;
...
END;
ORA-06550: line 14, column 60:
PLS-00382: expression is of wrong type
ORA-06550: line 13, column 60:
PLS-00382: expression is of wrong type
ORA-06550: line 13, column 60:
PL/SQL: ORA-00904: : invalid identifier
ORA-06550: line 12, column 7:
PL/SQL: SQL Statement ignored
Am I missing something obvious? Is there a way to make this statement work?
I have tried using different methods but succeeded with the only following:
Method 1:
Using particular record names from %ROWTYPE field
DECLARE
mProperty1 MyTable%ROWTYPE;
mProperty2 MyTable%ROWTYPE;
BEGIN
mProperty1.COL1 := 1;
mProperty2.COL1 := 2;
INSERT ALL
INTO MyTable VALUES (mProperty1.col1)
INTO MyTable VALUES (mProperty2.col1)
SELECT 1 FROM DUAL;
END;
/
-- may be bad choice but you can use like this
Method 2:
If you are concerned about performance then you can also use this method:
DECLARE
TYPE mProperty_TYPE IS TABLE OF MyTable%ROWTYPE;
mProperty mProperty_TYPE;
BEGIN
mProperty := mProperty_TYPE();
mProperty.EXTEND(2);
mProperty(1).COL1 := 3;
mProperty(2).COL1 := 4;
FORALL I IN 1..mProperty.COUNT
INSERT INTO MyTable VALUES mProperty(I);
END;
/
db<>fiddle demo
Im trying to create button in Apex which execute given procedure. As input values I gave two date fields called Poczatek and Koniec. The button should execute this procedure on submit. It perfectly works in Oracle, but throw a lot of errors in Apex.
set serveroutput on
create or replace procedure KORELACJA(:Poczatek, :Koniec)
IS
miasto VARCHAR(25);
korelacja NUMBER;
cursor c1 is
SELECT TEMP.nazwa, corr(TEMP.temperatura, WILGOTNOSC.wilg)
FROM TEMP INNER JOIN WILGOTNOSC
on TEMP.nazwa = WILGOTNOSC.nazwa
and TEMP.data = WILGOTNOSC.data
WHERE TEMP.data between to_date(:Poczatek, 'YYYY-MM-DD') and to_date(:Koniec, 'YYYY-MM-DD')
GROUP BY TEMP.nazwa;
BEGIN
DBMS_OUTPUT.put_line(RPAD('Miasto',10)||RPAD('Korelacja',10));
open c1;
FOR i IN 1..6
LOOP
commit;
fetch c1 into miasto, korelacja;
DBMS_OUTPUT.put_line(RPAD(miasto,10)||RPAD(korelacja,10));
END LOOP;
close c1;
END KORELACJA;
/
Errors look like this:
1 error has occurred
ORA-06550: line 2, column 5: PL/SQL: ORA-00922: missing or invalid option
ORA-06550: line 2, column 1: PL/SQL: SQL Statement ignored ORA-06550: line 6,
column 11: PLS-00103: Encountered the symbol "NUMBER" when expecting one of the
following: := . ( # % ; ORA-06550: line 9, column 18: PLS-00103: Encountered the symbol "JOIN" when
expecting one of the following: , ; for group having intersect minus order start
union where connect
Anyone knows the solution?
I'd suggest you to leave the procedure in the database; call it from Apex.
As you said that it works OK, I'm not going to examine the code. Just modify the first line:
create or replace procedure KORELACJA(par_Poczatek in date,
par_Koniec in date)
is ...
Then, in Apex process, call the procedure as
korelacja(:p1_poczatek, :p2_koniec);
Note that you might need to apply TO_DATE function to those items, using appropriate format mask, such as
korelacja(to_date(:p1_poczatek, 'dd.mm.yyyy',
to_date(:p1_koniec , 'dd.mm.yyyy');
If you insist on keeping the procedure in Apex' process (I wouldn't recommend it), the you don't need CREATE PROCEDURE but an anonymous PL/SQL block. It won't accept any parameters - use Apex items directly.
declare
miasto VARCHAR(25);
korelacja NUMBER;
cursor ...
WHERE TEMP.data between to_date(:p1_Poczatek, 'YYYY-MM-DD') ...
begin
...
end;