is there a better way to write avg procedure? - oracle

I wrote this procedure but it keeps showing "WARNING: Procedure creates with compilation errors" and I don't know why, here's the table I created
create table EnrolledInClasses (
St_Id char(9) primary key,
C_Id char(6),
GradeN Number(2),
constraint FK_StId Foreign key (St_Id) references Student (St_Id),
constraint FK_CoID Foreign key (C_Id) references course (C_Id)
constraint CheckGrade check (Grade>-1)
);
and here's the procedure:
CREATE OR REPLACE PROCEDURE Avg_grades
IS
avg_grades NUMBER := 0;
BEGIN
SELECT AVG (Grade)
INTO avg_grades
FROM EnrolledInClasses
dbms_output.put_line('The average of grades is :'||avg_grades);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line ('No Data Is Found..');
END;
/

Since you are using PL/SQL Developer, make sure you are writing your code in a Procedure window. Then the compilation error will be clearly highlighted. In this case it's the missing semicolon at the end of the select into statement.
Or in SQL*Plus you can use show errors:
Warning: Procedure created with compilation errors.
SQL> show errors
Errors for PROCEDURE AVG_GRADES:
LINE/COL ERROR
-------- -----------------------------------------------------------------
6/1 PL/SQL: SQL Statement ignored
9/12 PL/SQL: ORA-00933: SQL command not properly ended
or the full syntax, show errors procedure avg_grades.
Or you can directly query dba|all|cdb|user_errors:
select * from user_errors e
where e.name = 'AVG_GRADES'
and e.attribute = 'ERROR';
Also, the table definition could be written more succinctly as:
create table enrolledinclasses
( st_id primary key constraint fk_stid references student(st_id)
, c_id constraint fk_coid references course (c_id)
, grade number(2) constraint checkgrade check (grade>-1)
);
student.st_id and course.c_id should probably be numbers or integers, or at least varchar2, and not char.
It is good practice to use code indentation to align code into blocks showing their dependency structure. That would give something like this:
create or replace procedure avg_grades as
avg_grades number := 0;
begin
select avg(grade) into avg_grades
from enrolledinclasses;
dbms_output.put_line('The average grade is: ' || avg_grades);
exception
when no_data_found then
dbms_output.put_line('No Data Is Found.');
end;
You might want to round() that average before displaying it, as by default it could be displayed with a lot more decimal places than you might want.
Also, it's not possible to get a no_data_found exception from a single-group aggregate query like this. If there are no rows in the table you will get a null value as the result, not an error.

Related

ORA-00907: Missing right parenthesis with procedure returning table

I tried to right a procedure to return a table.
that procedure should return a list with an employee info when emp_num parameter equal SSN otherwise return list with all employees info.
First I create a record as below:
CREATE OR REPLACE TYPE emp_record IS OBJECT(emp_lname VARCHAR2(30),
emp_ssn CHAR(9),
emp_sal NUMBER(6));
Then I create table of type emp_record as below:
CREATE OR REPLACE TYPE emp_table IS TABLE OF emp_record;
Then I create a procedure get_employee with two parameters:
CREATE OR REPLACE PROCEDURE get_employee(emp_num IN NUMBER , output_emp OUT emp_table) AS
CURSOR emp_cur IS
SELECT LNAME,SSN,SALARY
FROM EMPLOYEE
WHERE NVL((emp_num = SSN),(SSN = SSN));
BEGIN
IF NOT (emp_cur%ISOPEN) THEN
OPEN emp_cur;
END IF;
LOOP
FETCH emp_cur BULK COLLECT INTO output_emp;
EXIT WHEN output_emp.count=0;
CLOSE emp_cur;
END LOOP;
END;
And when I run that code the below error has appear:
[Warning] ORA-24344: success with compilation error
6/20 PL/SQL: ORA-00907: missing right parenthesis
4/1 PL/SQL: SQL Statement ignored
(1: 0): Warning: compiled but with compilation errors
You have used the alien code here, There is a syntax error:
WHERE
NVL(
(EMP_NUM = SSN),
(SSN = SSN)
);
NVL can take two columns/constants as an input parameter, not a boolean.
You want a WHERE condition - either EMP_NUM is full or if it is not null then it is equal to SSN then You need to use something like this:
WHERE
NVL(EMP_NUM,SSN) = SSN;
or better to use OR condition as follows:
WHERE EMP_NUM IS NULL OR EMP_NUM = SSN;
Please refer Oracle Documentation for NVL syntax. Based on similarity of syntax I feel you probably want to use DECODE instead. If so CASE is the way to go.

Create a procedure to copy some records from a table to another

I'm trying to create a simple procedure to copy some records from Table1 to Table2.
Table1:
id number PK
operation varchar2(50)
position varchar2(50)
code_operation varchar2(50) FK
Table2:
code_operation varchar2(50) PK
operation varchar2(50)
position varchar2(50)
client_number varchar2(50)
Starting from the client_number I have to copy the associated operation and position from Table1 and insert into operation and position of Table2.
I've tried this code but it doesn't work:
CREATE OR REPLACE PROCEDURE COPY_DATA(
BEGIN
DECLARE P_CLIENT_NUMBER VARCHAR2(50);
DECLARE P_CODE_OPERATION VARCHAR2(50);
DECLARE P_DIVISION VARCHAR2(50);
DECLARE P_POSITION VARCHAR2(50)
SELECT CODE_OPERATION INTO P_CODE_OPERATION FROM TABLE2;
SELECT CLIENT_NUMBER INTO P_CLIENT_NUMBER FROM TABLE2;
SELECT DIVISION INTO P_DIVISION FROM TABLE1;
SELECT POSITION INTO P_POSITION FROM TABLE1;
INSERT INTO TABLE2(DIVISION,POSITION)
WHERE CODE_OPERATION=P_COD_OPERATION;
END
);
I've got this error ERROR PLS-00103: Encountered the symbol “DECLARE” but I don't understand why, plus with this error I don't know if my code is correct or not.
Error you got is easy to fix. PL/SQL block has declare-begin-exception-end structure, which means that you first declare variables (in a stored procedure, you don't use declare keyword; strange enough, for triggers - which as kind of stored as well - you do use it). For example:
create or replace procedure copy_data as
p_client_number table2.client_number%type;
begin
select client_number
into p_client_number
from table2;
exception
when too_many_rows then
raise;
end;
However, without WHERE clause (or an aggregate, such as MIN or MAX), this will raise too_many_rows exception because it'll try to fetch all client numbers into a scalar variable, and that won't work. I included the way which shows how to handle it. What will you really do? Restrict number of rows to 1. Similarly, you'd handle no_data_found or other exceptions.
What you described looks strange to me.
you want to insert into table2 - which is a master table
values you'd insert are in table1 - which is a detail table
Looking at tables, I'd say that it should be vice versa.
insert into table1 (id, operation, position, code_operation)
select seq.nextval,
b.operation,
b.position,
b.code_operation
from table2 b
where b.client_number = 'ABC';
(I presumed that primary key is populated via a sequence.)
It also means that none of variables you declared is necessary. But, the procedure would accept client number as a parameter.
The whole procedure would then be:
create or replace procedure copy_data
(par_client_number in table2.client_number%type)
is
begin
insert into table1 (id, operation, position, code_operation)
select seq.nextval,
b.operation,
b.position,
b.code_operation
from table2 b
where b.client_number = par_client_number;
end;
You'd call it as
begin
copy_data('ABC');
end;
/

Why am I getting this error: "ORA-00922: missing or invalid option" after running a PL/SQL procedure?

I created a table Patient with some attributes, like p_name, p_surname, p_number... I would like to create a procedure to transfer a patient from this table Patient to another table (Patient_backup), case his "p_number" attribute has received an input, deleting it from the first table and remaining only in the second. The second table has the same structure of the first one. I have coded the procedure like that.
CREATE TABLE patient (
p_number VARCHAR2(10) NOT NULL,
p_name VARCHAR2(15),
p_surname VARCHAR2(15),
p_street VARCHAR2(20),
p_city VARCHAR2(15)
);
CREATE TABLE patient_backup (
p_number VARCHAR2(10) NOT NULL,
p_name VARCHAR2(15),
p_surname VARCHAR2(15),
p_street VARCHAR2(20),
p_city VARCHAR2(15)
);
CREATE [OR REPLACE] PROCEDURE transfer (p_number VARCHAR2)
AS
CURSOR k1 IS SELECT p_number FROM patient;
BEGIN
OPEN k1;
LOOP
FETCH k1 INTO p_number;
IF p_number IS NULL THEN
dbms_output.put_line('empty');
ELSE
INSERT INTO patient_backup (SELECT * FROM patient);
Execute Immediate 'Drop Table patient;';
END IF;
END LOOP;
CLOSE k1;
END transfer;
But when I run it,I get the error "ORA-00922: missing or invalid option". Could you help me with that? I wonder if the code is correct. I have read a material about PL/SQL, but the concepts were not connected to each other, so I just tried to gather everything together, and I hope it is correct. Could you help me to correct this code and make it work?
It's hard to tell where exactly the error is, but my guess is: remove the ; from inside the string for execute immediate.
But I think you want do not want to DROP the table - that removes the table completely from the database including all rows and its definition. It won't be accessible after that.
I think what you really want is to DELETE a row from that table, not remove the table completely.
Also: the whole loop is completely unnecessary (and inefficient). You can do that with two simple SQL statements:
insert into patient_backup
select *
from patient
where p_number = 42; --<< to pick one patient
delete from patient
where p_number = 42;
Putting that into a procedure:
CREATE PROCEDURE transfer (p_number_to_delete VARCHAR2)
AS
BEGIN
insert into patient_backup
select *
from patient
where p_number = p_number_to_delete;
delete from patient
where p_number = p_number_to_delete;
END transfer;
It's highly recommended to not use the name of a column as the name of a parameter. That's why I named the parameter p_number_to_delete (but p_number is a bad name for a column that isn't a number to begin with - but that's a different discussion)
I think you need to DECLARE your cursor before you define it.
In Your procedure code have some error
1.P_NUMBER input parameter cannot be used into statment
2.don't use semicolon inside the EXECUTE IMMEDIATE string
3. in loop statement you should use exit otherwise it will run
continuously
Here the code
CREATE OR REPLACE PROCEDURE TRANSFER (P_NUMBER IN VARCHAR2) AS
CURSOR K1 IS
SELECT P_NUMBER FROM PATIENT;
P_NUM PLS_INTEGER;
BEGIN
OPEN K1;
LOOP
FETCH K1 INTO P_NUM;
IF P_NUM IS NULL THEN
DBMS_OUTPUT.PUT_LINE('EMPTY');
ELSE
INSERT INTO PATIENT_BACKUP (SELECT * FROM PATIENT);
DELETE FROM PATIENT;
END IF;
EXIT WHEN P_NUM IS NULL;
END LOOP;
CLOSE K1;
END TRANSFER;

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.

PLS-00201 - identifier must be declared

I executed a PL/SQL script that created the following table
TABLE_NAME VARCHAR2(30) := 'B2BOWNER.SSC_Page_Map';
I made an insert function for this table using arguments
CREATE OR REPLACE FUNCTION F_SSC_Page_Map_Insert(
p_page_id IN B2BOWNER.SSC_Page_Map.Page_ID_NBR%TYPE,
p_page_type IN B2BOWNER.SSC_Page_Map.Page_Type%TYPE,
p_page_dcpn IN B2BOWNER.SSC_Page_Map.Page_Dcpn%TYPE)
I was notified I had to declare B2BOWNER.SSC_Page_Map prior to it appearing as an argument to my function. Why am I getting this error?
EDIT: Actual error
Warning: compiled but with compilation errors
Errors for FUNCTION F_SSC_PAGE_MAP_INSERT
LINE/COL ERROR
-------- -----------------------------------------------------------------
2/48 PLS-00201: identifier 'SSC_PAGE_MAP.PAGE_ID_NBR' must be declared
0/0 PL/SQL: Compilation unit analysis terminated
EDIT: Complete PL/SQL Function
RETURN INTEGER
IS
TABLE_DOES_NOT_EXIST exception;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942); -- ORA-00942
BEGIN
INSERT INTO
B2BOWNER.SSC_Page_Map VALUES(
p_page_id,
p_page_type,
p_page_dcpn);
RETURN 0;
EXCEPTION
WHEN TABLE_DOES_NOT_EXIST THEN
RETURN -1;
WHEN DUP_VAL_ON_INDEX THEN
RETURN -2;
WHEN INVALID_NUMBER THEN
RETURN -3;
WHEN OTHERS THEN
RETURN -4;
END;
SHOW ERRORS PROCEDURE F_SSC_Page_Map_Insert;
GRANT EXECUTE ON F_SSC_Page_Map_Insert TO B2B_USER_DBROLE;
RETURN INTEGER
EDIT: I change the arguments and received a new error related to the insert command
CREATE OR REPLACE FUNCTION F_SSC_Page_Map_Insert(
p_page_id IN INTEGER,
p_page_type IN VARCHAR2,
p_page_dcpn IN VARCHAR2)
RETURN INTEGER
IS
TABLE_DOES_NOT_EXIST exception;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942); -- ORA-00942
BEGIN
INSERT INTO
B2BOWNER.SSC_Page_Map VALUES(
p_page_id,
p_page_type,
p_page_dcpn);
The error
Errors for FUNCTION F_SSC_PAGE_MAP_INSERT
LINE/COL ERROR
-------- -----------------------------------------------------------------
17/18 PL/SQL: ORA-00942: table or view does not exist
16/5 PL/SQL: SQL Statement ignored
The tables has been verified within the correct schema and with the correct attribute names and types
EDIT: I executed the following command to check if I have access
DECLARE
count_this INTEGER;
BEGIN
select count(*) into count_this
from all_tables
where owner = 'B2BOWNER'
and table_name = 'SSC_PAGE_MAP';
DBMS_OUTPUT.PUT_LINE(count_this);
END;
The output I received is
1
PL/SQL procedure successfully completed.
I have access to the table.
EDIT:
So I finally conducted an insert into the table via the schema using PL/SQL and it worked fine. It appears I simply do not have authority to create functions but that is an assumption.
EDIT:
Actual table DDL statement
v_create := 'CREATE TABLE ' || TABLE_NAME || ' (
PAGE_ID_NBR NUMERIC(10) NOT NULL Check(Page_ID_NBR > 0),
PAGE_TYPE VARCHAR2(50) NOT NULL,
PAGE_DCPN VARCHAR2(100) NOT NULL,
PRIMARY KEY(Page_ID_NBR, Page_Type))';
EXECUTE IMMEDIATE v_create;
COMMIT WORK;
COMMIT COMMENT 'Create Table';
When creating the TABLE under B2BOWNER, be sure to prefix the PL/SQL function with the Schema name; i.e. B2BOWNER.F_SSC_Page_Map_Insert.
I did not realize this until the DBAs pointed it out. I could have created the table under my root USER/SCHEMA and the PL/SQL function would have worked fine.
The procedure name should be in caps while creating procedure in database.
You may use small letters for your procedure name while calling from Java class like:
String getDBUSERByUserIdSql = "{call getDBUSERByUserId(?,?,?,?)}";
In database the name of procedure should be:
GETDBUSERBYUSERID -- (all letters in caps only)
This serves as one of the solutions for this problem.
you should give permission on your db
grant execute on (packageName or tableName) to user;

Resources