Oracle sql - Errors creating a procedure - sql

I've created this procedure
create or replace procedure enrollstudent(
rno in STUDENTS.ROLL_NUMBER%type,
sname in STUDENTS.STUDENT_NAME%type,
cid in STUDENTS.COURSEID%type,
cfees in STUDENTS.FEES%type,
spercent in students.percentage%type
)as
discount number;
begin
discount := spercent*5;
cfees := cfees-discount;
insert into STUDENTS values(rno, sname, cid, cfees, spercent);
commit;
end;
that works with this table
CREATE TABLE STUDENTS(
ROLL_NUMBER NUMBER(20) NOT NULL,
STUDENT_NAME VARCHAR2(25) NOT NULL,
COURSEID NUMBER(20) NOT NULL,
FEES NUMBER(20) ,
PERCENTAGE NUMBER(20)
);
When I run the procedure creation I have a Procedure ENROLLSTUDENT compiled and it gets created but i have the following errors in the compiler log
Error(8,1): PL/SQL: Statement ignored
Error(8,1): PLS-00363: expression 'CFEES' cannot be used as an assignment target
If I try to run the procedure with some data I simple have PLS-00905: object [schema].ENROLLSTUDENT is invalid

CFEES is an IN parameter. You can't change it. Create a local variable, and
use that, example:
cfees2 NUMBER := cfees;
cfees2 := cfees2 - discount;
insert into STUDENTS values(rno, sname, cid, cfees2, spercent);

You don't need to define and calculate any local variables; you can do the computation of cfees in place (right in the INSERT statement):
create or replace procedure enrollstudent(
rno in STUDENTS.ROLL_NUMBER%type,
sname in STUDENTS.STUDENT_NAME%type,
cid in STUDENTS.COURSEID%type,
cfees in STUDENTS.FEES%type,
spercent in students.percentage%type
)as
begin
insert into STUDENTS values(rno, sname, cid, cfees-spercent*5, spercent);
commit;
end;
/

Related

Insert into a table using a stored procedure

I have this simple stored procedure, where it would add a column to my Orders table
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
DECLARE
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1;
BEGIN
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C')
END ADD_ORDER;
It is saying the the declare part is not at the correct place (I think), and also it should not end there. Here is what it is saying at the error screen:
Error(6,1): PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following:
begin function pragma procedure subtype type current cursor delete exists prior external language The symbol "begin was inserted before "DECLARE" to continue.
Error(11,1): PLS-00103: Encountered the symbol "END" when expecting one of the following: , ; return returning
Can anyone tell me what is going wrong here ?
As has been mentioned, it is a bad idea to select the maximum order number and then use that to insert a row. If two processes do this at the same time, they try to insert rows with the same order number.
Better use Oracle's built-in features SEQUENCE or IDENTITY.
Here is how you could create the table:
CREATE TABLE orders
(
order_no NUMBER(8) GENERATED ALWAYS AS IDENTITY,
rep_no NUMBER(3) DEFAULT 36 NOT NULL,
cust_no NUMBER(8) NOT NULL,
order_dt DATE DEFAULT SYSDATE NOT NULL,
status VARCHAR2(1) DEFAULT 'C' NOT NULL
);
And this is what your procedure would look like then:
CREATE OR REPLACE PROCEDURE add_order
(
in_cust_no IN NUMBER,
out_order_no OUT NUMBER
) AS
BEGIN
INSERT INTO ORDERS(cust_no) VALUES (in_cust_no)
RETURNING order_no INTO out_order_no;
END add_order;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4b49723c15eb810c01077286e171bc95
There is a syntax error in your code.
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1; --not be used liked it.
Use below code
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
V_NEW_ORDER_ID NUMBER;
BEGIN
SELECT NVL(MAX(ORDER_NO),0)+1 INTO V_NEW_ORDER_ID FROM ORDERS;
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( V_NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C');
NEW_ORDER_ID:=V_NEW_ORDER_ID;
/*
* CURDATE() -> I am assuming it is user defined function. You can also use SYSDATE, CURRENT_DATE istead of CURDATE()
* OUT Parameter is a write-only parameter. You cannot read value from OUT Parameter
*/
END ADD_ORDER;
Few things need to be correct.
If you're expecting to write a PROCEDURE or a FUNCTION you don't have to use the DECLARE keyword. In writing a test script or something, you should use the DECLARE keyword to declare variables.
When writing a procedure,
All the parameters should be inside the brackets.
Variables should define between AS and BEGIN keywords and should give the datatype.
If you need to fetch the MAX number of ORDERS TAB you have to write a SQL query for that. Because the MAX function only can be used inside a SQL. Additionally, if you interest there is an in-built feature call SEQUENCE in ORACLE which can use for NEW_ORDER_ID. You can check with the link below.
adding a sequence for oracle plsql
I did some changes to your code. Hope it's working fine now. Please take a visit here.
CREATE or REPLACE PROCEDURE ADD_ORDER (
CUSTOMER_ID IN NUMBER
NEW_ORDER_ID OUT NUMBER
) AS
CURSOR get_max_order_no IS
SELECT MAX(order_no)
FROM ORDERS;
rec_ NUMBER := 0;
BEGIN
OPEN get_max_order_no;
FETCH get_max_order_no INTO rec_;
CLOSE get_max_order_no;
NEW_ORDER_ID := rec_ + 1;
INSERT INTO ORDERS
(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES
(NEW_ORDER_ID, 36, CUSTOMER_ID, SYSDATE, 'C');
END ADD_ORDER;

please find the solution of this problem. ( in PL SQL ) --------

I'm doing an assignment and the last question states that I call the procedure from question 2 and function from question 3 and then put them in an insert clause(question 4).
Write a PL/SQL Code for an anonymous block to do the following (in package):
a) Get the doctor name for a given doctor id (read from keyboard)
using a function [FunGetDoctorName].
b) Get the department name for a given department id (read from
keyboard) using a procedure [ProGetepartmentName].
c) Insert the doctor name and department name (which you got from the
function and
procedure) by including serial number and current date into the
visitings table.
put the answer in ( Package)
this is my table codes:
CREATE TABLE DEPT(
DeptNo NUMBER PRIMARY KEY,
DeptName VARCHAR2(30) NOT NULL);
CREATE TABLE DOCTORS(
DoctorID NUMBER PRIMARY KEY,
DoctorNAME VARCHAR2(30) NOT NULL,
DeptNo NUMBER REFERENCES DEPT(DEPTNO),
Salary NUMBER NOT NULL);
CREATE TABLE VISITINGS(
SlNo NUMBER PRIMARY KEY,
DoctorName VARCHAR2(30),
DepartmentName VARCHAR2(30),
VisitDate DATE);
INSERT INTO DEPT VALUES(10,'ENT');
INSERT INTO DEPT VALUES(20,'Orthopedic');
INSERT INTO DEPT VALUES(30,'Cardiology');
INSERT INTO DEPT VALUES(40,'Neurology');
INSERT INTO DOCTORS VALUES(101,'Abheer',20,2550);
INSERT INTO DOCTORS VALUES(102,'Zuwaina',10,2175);
INSERT INTO DOCTORS VALUES(103,'Sara',30,1985);
INSERT INTO DOCTORS VALUES(104,'Fatma',20,2200);
INSERT INTO DOCTORS VALUES(105,'Laila',10,2600);
INSERT INTO VISITINGS VALUES(1,'Sara','Cardiology','10-Nov-19');
INSERT INTO VISITINGS VALUES(2,'Abheer','Orthopedic','11-Nov-19');
My function
create or replace function FunGetDoctorName(Docid number) return varchar2 is
docname DOCTORS.DoctorName%type;
Begin
select DoctorName into docname from DOCTORS where DoctorID = Docid;
return docname;
End ;
/
My procedure
create or replace procedure ProGetDepartmentName is
depname dept.DeptName%type;
Begin
select DeptName into depname from dept where DeptNo =10;
dbms_output.put_line(depname);
End ;
/
here is the problem:
Create or replace package pkg1 is
Function FunGetDoctorName(Docid Number) return varchar2 ;
procedure ProGetDepartmentName(DeptNo NUMBER);
end pkg1;
/
CREATE OR REPLACE PACKAGE BODY pkg1 AS
FUNCTION FunGetDoctorName(Docid Number)
RETURN varchar2 IS
docname DOCTORS.DoctorName%type;
BEGIN
select DoctorName into docname from DOCTORS where DoctorID = Docid;
return docname ;
END;
PROCEDURE ProGetDepartmentName(DeptNo NUMBER) IS
depname dept.DeptName%type;
BEGIN
Select DeptName into depname from dept where DeptNo=10;
dbms_output.put_line(depname) ;
END;
END pkg1 ;
/
declare
ProGetDepartmentName
(:DeptNo in dept.DeptNO%type,
depname in dept.DeptName%type)
FunGetDoctorName
(:Docid in DOCTORS.DoctorID%type ,
docname in DOCTORS.DoctorName%type);
docname varchar2(30);
depname varchar2(30);
Docid number;
serial number;
is
Begin
dbms_output.put_line('Department Name: '||depname);
select count(slno) into serial from visitings;
serial :=serial+1;
insert into visitings(slno,doctorname,departmentname,visitdate) values(serial,docname,depname,sysdate);
End;
/
I keep getting errors :
SP2-0552: Bind variable "DOCID" not declared.
First of all, your procedure must have one in and one out parameter so that you can pass deptno and get deptname as the output.
procedure ProGetDepartmentName(p_depnum number,P_depname out varchar)
is
Begin
select DeptName into P_depname from dept where DeptNo = p_depnum;
dbms_output.put_line(p_depnum);
End ;
/
In your pl/sql block, you can use substitution variable to take an input from keyboard as follows:
declare
V_DeptNo dept.DeptNO%type := &dept_no
V_deptname in dept.DeptName%type;
V_Docid in DOCTORS.DoctorID%type := &doc_id;
is
Begin
Pkg1.ProGetDepartmentName(v_deptno, v_deptname);
dbms_output.put_line('Department Name: '|| v_deptname);
insert into visitings(slno,doctorname,departmentname,visitdate)
values((select count(slno) + 1 from visitings),FunGetDoctorName(v_docid),v_deptname,sysdate);
End;
/
Note:
Instead of fetching count from table and adding one into it for slno, you should use the sequence.
In procedure and function, use exception block to handle no row found or multiple record found or any other issues gracefully.
While inserting in to table, you should also use exceprion block to handle issues gracefully. (In your case it is needed because of the count(slno) + 1 logic as it can assign same number to different sessions executing simentaneously which my lead to primary key violation)

Insert Data Into Table with Stored Procedure in Oracle SQL

I am working through a homework assignment and am stuck on a problem with stored procedures.
I have the following tables:
create table Restaurant(rID int, name varchar2(100), address varchar2(100), cuisine varchar2(100));
create table Reviewer(vID int, name varchar2(100));
create table Rating(rID int, vID int, stars int, ratingDate date);
For my stored procedure I need to create, I get an input of a restaurant name (unique), reviewer name(unique), star rating, and review date. I need to update the Rating table with the proper information, and add a new reviewer to the Review table if they have not previously existed in the table.
In order to start my stored procedure, I wanted to start with just creating a new table called newReview to get the inputs stored in a new table, before re-writting to update the existing tables.
Here is the newReview Table
CREATE TABLE newReview(
RestaurantName VARCHAR(100),
UserName VARCHAR(100),
Stars Int,
RatingDate Date
)
This is my AddNewReview procedure, which compiles with success:
CREATE OR REPLACE PROCEDURE AddNewReview(
RESTAURANTNAME IN VARCHAR2 (100)
, USERNAME IN VARCHAR2 (100)
, Stars IN NUMBER
, RATINGDATE IN DATE
) AS
BEGIN
INSERT INTO newReview VALUES (RestaurantName, UserName, Stars, RatingDate);
END AddNewReview;
;
However, when I run the stored procedure with inputs as such,
BEGIN
AddNewReview ('Rangoli', 'Sarah M.', 4, '2020-11-21');
END;
I get the following error:
Error starting at line : 20 in command -
BEGIN
AddNewReview ('Rangoli', 'Sarah M.', 4, '2020-11-21');
END;
Error report -
ORA-06550: line 2, column 5:
PLS-00905: object TOCONN22.ADDNEWREVIEW is invalid
ORA-06550: line 2, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have tried defining the date input as DATE '2020-11-21' and also switching the single quote to double. Where am I going wrong, as this is the first stored procedure I am writing.
Try to change Stars data type from NUMBER to Int
AS:
CREATE OR REPLACE PROCEDURE AddNewReview(
RESTAURANTNAME IN VARCHAR2 (100)
, USERNAME IN VARCHAR2 (100)
, Stars IN NUMBER
, RATINGDATE IN DATE
) AS
BEGIN
INSERT INTO newReview VALUES (RestaurantName, UserName, Stars, RatingDate);
END AddNewReview;
;
to
CREATE OR REPLACE PROCEDURE AddNewReview(
RESTAURANTNAME IN VARCHAR2 (100)
, USERNAME IN VARCHAR2 (100)
, Stars IN Int
, RATINGDATE IN DATE
) AS
BEGIN
INSERT INTO newReview VALUES (RestaurantName, UserName, Stars, RatingDate);
END AddNewReview;
;
You need to look up the ids for the insertion:
CREATE OR REPLACE PROCEDURE AddNewReview (
in_RESTAURANTNAME IN VARCHAR2(100),
in_USERNAME IN VARCHAR2(100),
in_Stars IN NUMBER,
in_RATINGDATE IN DATE
) AS
BEGIN
INSERT INTO newReview (rid, vid, stars, RatingDate)
SELECT r.id, rr.id, in_stars, in_RatingDate
FROM restaurant r JOIN
reviewer rr
ON r.name = in_RestaurantName AND
rr.name = in_UserName
END AddNewReview;
This joins to the reference tables to get the ids you need. It will not insert the review if either name does not match. Your question doesn't specify what to do in that case, so that seems like reasonable behavior.
Note that the parameters are named so they don't conflict with column names. And this has listed all columns for the insert -- both are best practices.
Parameters are defined only by name and data type, they do not contain size specification. So your procedure needs:
create or replace procedure addnewreview(
restaurantname in varchar2
, username in varchar2
, stars in int
, ratingdate in date
...
You need to use To_Date function while calling the stored procedure, like below
BEGIN
AddNewReview ('Rangoli', 'Sarah M.', 4, TO_DATE('2020-11-21','YYYY-MM-DD'));
END;

Implementing and testing a stored PL/SQL procedure

I'm trying to to insert a row into APPLICANT table and to generate a warning whenever a new applicant has the same values of all attributes except applicant number (a#) as an applicant already recorded in a table APPLICANT. Pass the values of attributes through the input parameters of a stored procedure INSAPP.
Here is my code:
CREATE OR REPLACE PROCEDURE INSAPP(
a_num IN NUMBER,
a_fname IN VARCHAR,
a_lname IN VARCHAR,
a_address IN VARCHAR,
a_city IN VARCHAR,
a_state IN VARCHAR,
a_phone IN NUMBER,
a_fax IN NUMBER,
a_email IN VARCHAR,
a_acomment IN LONG) IS
app_row APPLICANT%ROWTYPE;
FAIL EXCEPTION;
BEGIN
--VERIFY APPLICANT FNAME
SELECT *
INTO app_row
FROM APPLICANT
WHERE fname = a_fname AND
lname = a_lname AND
address = a_address AND
city = a_city AND
state = a_state AND
phone# = a_phone AND
fax# = a_fax AND
email = a_email AND
acomment = a_acomment;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('CREATING APPLICANT');
BEGIN
SELECT MAX(a#)+1
INTO a_num
FROM APPLICANT;
INSERT INTO APPLICANT VALUES (a_num,a_fname,a_lname,a_address,a_city,a_state,a_phone,a_fax,a_email,a_acomment);
COMMIT;
DBMS_OUTPUT.PUT_LINE('APPLICANT HAS SUCCESSFULLY ADDED');
EXCEPTION
WHEN Fail THEN
ROLLBACK;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
ROLLBACK;
END;
END;
END INSAPP;
/
But when i trying to execute it with this code,it give me an error message like this
Warning: Pcedure created with compilation errors.
Then i trying to show the erros:
- 49/1 PLS-00103: Encountered the symbol "END"
Did i make any mistake on the pl/sql code? Please correct me as i'm still new to pl/sql. Thanks and very appreciate that. :)
if you execute it from sqlplus, please try
EXEC INSAPP ( 000001, 'PETER', 'JONES', '7 STATION ST.', 'PERTH', 'WA', 645278453, NULL, NULL, 'Job expectations ? Money, money, money, ...' );
or wrap it inside begin .. end block
begin
INSAPP ( 000001, 'PETER', 'JONES', '7 STATION ST.', 'PERTH', 'WA', 645278453, NULL, NULL, 'Job expectations ? Money, money, money, ...' );
end;
/
UPD.
I think that you procedure is not compiled.
You cannot change value of input parameter a_num (by the way, it is not best to get new id for table, use sequences)
SELECT MAX(a#)+1
INTO a_num
FROM APPLICANT;
You have unnecessary END; after rollback statement in when section
Due to fact that compare null values always return false it is not good to search record by this condition
WHERE fname = a_fname AND
lname = a_lname AND
address = a_address AND
city = a_city AND
state = a_state AND
phone# = a_phone AND
fax# = a_fax AND
email = a_email AND
acomment = a_acomment;

PL/SQL error question

I am trying to write a stored procedure that inserts a row into an employee table. If the department does not exist, that department needs to be inserted into the departments table. I have the following code:
drop table employees;
drop table departments;
create table departments(
dept varchar2(30),
dept_number number,
dept_city varchar2(30),
CONSTRAINT pk_dept PRIMARY KEY(dept)
);
create table employees(
dept varchar2(30),
employee_name varchar2(40),
employee_id number,
CONSTRAINT pk_id PRIMARY KEY(employee_id),
CONSTRAINT fk_dept FOREIGN KEY (dept) REFERENCES departments(dept)
);
CREATE OR REPLACE PROCEDURE employeeadd(
a_dept IN VARCHAR2,
a_employee_name IN VARCHAR2,
a_employee_id IN NUMBER)
as
li_count NUMBER;
BEGIN
sp_check_dept(a_dept, li_count);
if li_count = 0 then
INSERT INTO departments (dept) values (a_dept);
return;
end if;
INSERT INTO employee values (a_dept, a_employee_name, a_employee_id);
end;
/
create or replace procedure sp_check_dept(a_dept IN NUMBER,
a_count OUT NUMBER)
as
begin
select count(*)
into a_count
from departments
where dept_number = a_dept;
end;
/
When I run my execute statement as execute employeeadd('marketing', 'john', 10); I get the following errors. I can't seem to figure out how to get past the errors and/or write this correctly:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "employeeadd", line 8
ORA-06512: at line 1
Why is li_count declared outside the BEGIN...END block? Do you need to assign it before sending it as an argument to sp_check_dept()?
Edit: Just saw your followup comment: sp_check_dept is expecting a number as its first parameter; you have declared a_dept as VARCHAR.
sp_check_dept takes a department number an input parameter (a NUMBER) and returns a count as an ouput parameter. employeeadd is passing a department name (a VARCHAR2) as the first parameter to sp_check_dept. There are a couple of ways to fix this. In general, you'll want a more consistent method of naming parameters to make it easier to identify these problems.
Option 1: Use the department name for both functions
create or replace procedure sp_check_dept(p_dept_name IN departments.dept%type,
p_count OUT NUMBER)
as
begin
select count(*)
into p_count
from departments
where dept = p_dept_name;
end;
/
CREATE OR REPLACE PROCEDURE employeeadd(
p_dept_name IN departments.dept%type,
p_employee_name IN employees.employee_name%type,
p_employee_id IN employees.employee_id%type)
as
li_count NUMBER;
BEGIN
sp_check_dept(p_dept_name, li_count);
if li_count = 0 then
INSERT INTO departments (dept)
VALUES (p_dept_name);
end if;
INSERT INTO employee(dept, employee_name, employee_id)
VALUES (p_dept, p_employee_name, p_employee_id);
end;
/
Option 2: Convert the department name in employeeAdd to the department number before passing it to sp_check_dept
create or replace procedure sp_check_dept(p_dept_number IN departments.dept_number%type,
p_count OUT NUMBER)
as
begin
select count(*)
into p_count
from departments
where dept_number = p_dept_number;
end;
/
CREATE OR REPLACE FUNCTION get_dept_number( p_dept_name IN departments.dept%tyep )
RETURN departments.dept_number%type
IS
l_dept_number departments.dept_number%type;
BEGIN
SELECT dept_number
INTO l_dept_number
FROM departments
WHERE dept = p_dept_name;
RETURN l_dept_number
END;
/
CREATE OR REPLACE PROCEDURE employeeadd(
p_dept_name IN departments.dept%type,
p_employee_name IN employees.employee_name%type,
p_employee_id IN employees.employee_id%type)
as
li_count NUMBER;
BEGIN
sp_check_dept( get_dept_number(p_dept_name), li_count);
if li_count = 0 then
INSERT INTO departments (dept)
VALUES (p_dept_name);
end if;
INSERT INTO employee(dept, employee_name, employee_id)
VALUES (p_dept, p_employee_name, p_employee_id);
end;
/
A couple of other observations
I removed the RETURN statement from your IF statement in employeeAdd. You almost certainly do not want to exit the procedure after inserting a row into the DEPARTMENTS table before inserting the row into the EMPLOYEE table.
Your table definition used the plural EMPLOYEES. Your procedure used the singular EMPLOYEE. I did not correct that because I wasn't sure whether the DDL you posted was incorrect or whether the procedure you posted was incorrect.
It would, in general, make far more sense for sp_check_dept to be implemented as a function that returned the count rather than as a procedure with an OUT parameter. If a piece of code simply exists to return data to the caller, it should be declared as a function.
From a data model standpoint, the column name DEPT isn't particularly good. It would be far more appropriate to use something like DEPARTMENT_NAME that conveys what the column actually represents.
From a data model standpoint, having the VARCHAR2 column DEPT (even if it is renamed to DEPARTMENT_NAME) as the primary key of DEPARTMENTS and the foreign key in EMPLOYEES does not make much sense. The primary key should be immutable. However the name of the department will change over time. It would make far more sense for the DEPARTMENT_NUMBER to be the primary key and for the DEPARTMENT_NAME to simply be marked as unique. That will make it far easier when the Marketing department gets renamed Advertising in the future because you won't have to chase down all the child tables to update them.
You should pick a naming convention for procedures and stick with that. I would prefer check_dept and add_employee (verb followed by subject, underscores separating words, no prefix). But if you wanted sp_check_dept and sp_add_employee or checkDept and addEmployee or even sp_dept_check and sp_employee_add that would be fine. But you'll drive yourself, and the other developers, crazy if there is no pattern to your procedure naming conventions.
2 possibilities I can see:
1. the employee table has columns in a different order than your insert statement and it's trying to convert dept or name to the id
2. the value set into li_count isn't a number so it's trying to convert the return value to a number and giving you the error