please find the solution of this problem. ( in PL SQL ) -------- - 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)

Related

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;

Oracle sql - Errors creating a procedure

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;
/

Oracle PL/SQL: how to delete a row and then output it using pipe?

I have a package:
CREATE OR REPLACE PACKAGE pkg_location IS
TYPE dep_row IS RECORD (id departments.department_id%TYPE, name departments.department_name%TYPE);
TYPE dep_table IS TABLE OF dep_row;
FUNCTION drop_departments (v_city_title IN VARCHAR2) RETURN dep_table PIPELINED;
END pkg_location;
I need to implement drop_departments function, which deletes rows from the departments table by city and outputs deleted departments' data using pipes.
But I really don't know where to start from. Can you please help me?
Table structure:
DEPARTMENTS: DEPARTMENT_ID (NUMBER) | DEPARTMENT_NAME (VARCHAR 40) | LOCATION_ID (NUMBER)
LOCATIONS: LOCATION_ID (NUMBER) | CITY (VARCHAR 40)
you need 3 steps:
save all records that are going to be deleted into local table that has same type as function
delete the records
return the collection that you saved (and it's not necessary should be pipelined)
there's a problem with pipeline function - you cannot select * from table(my_pipe_function) and make delete (DML) operation inside the function during select.
to avoid the issue with delete inside function you need change logic and don't use pipeline OR you need delete records in autonomous transaction
see 3 samples below
insert into departments(id , department_id , department_name , city_title ) values(1, 1, '1', 'City-1');
insert into departments(id , department_id , department_name , city_title ) values(2, 2, '2', 'City-1');
insert into departments(id , department_id , department_name , city_title ) values(3, 2, '3', 'City-2');
sample 1 with pipeline
CREATE OR REPLACE PACKAGE BODY pkg_location IS
FUNCTION drop_departments (v_city_title IN VARCHAR2) RETURN dep_table PIPELINED
is
tblResult dep_table;
begin
--delete the records and save deleted records
delete from departments
where city_title = v_city_title
RETURNING department_id, department_name
BULK COLLECT INTO tblResult;
--return list of deleted records
for i in tblResult.FIRST .. tblResult.LAST loop
pipe row (tblResult(i));
end loop;
end;
END pkg_location;
but then you cannot use smth like this
select * from table( pkg_location.drop_departments('City-1'))
sample 2 - I removed pipelined
CREATE OR REPLACE PACKAGE BODY pkg_location IS
FUNCTION drop_departments (v_city_title IN VARCHAR2) RETURN dep_table
is
tblResult dep_table;
begin
--delete the records and save deleted records
delete from departments
where city_title = v_city_title
RETURNING department_id, department_name
BULK COLLECT INTO tblResult;
--return list of deleted records
return tblResult;
end;
END pkg_location;
and now you can use the function this way:
declare
tbl pkg_location.dep_table;
begin
tbl := pkg_location.drop_departments('City-1');
for i in tbl.first .. tbl.last loop
dbms_output.put_line(tbl(i).name);
end loop;
end;
output will be 1 and 2
sample 3 - using autonomous transactions
CREATE OR REPLACE PACKAGE BODY pkg_location IS
FUNCTION drop_departments (v_city_title IN VARCHAR2) RETURN dep_table PIPELINED
is
--it let you use the function in select * from table(f)
PRAGMA AUTONOMOUS_TRANSACTION;
tblResult dep_table;
begin
--delete the records and save deleted records
delete from departments
where city_title = v_city_title
returning department_id, department_name
bulk collect into tblResult;
--you must do the commit before pipe row ()
commit;
--return list of deleted records
for i in tblResult.FIRST .. tblResult.LAST loop
pipe row (tblResult(i));
end loop;
end;
END pkg_location;
now you can use
select * from table(pkg_location.drop_departments('City-1'))
result will be:
1 1 1
2 2 2

check age trigger from two separate tables in oracle sql

I need to create a trigger that checks that the age (in years) of the participant is higher than the minimum age of the race he/ she participates in. To do this I need to get information from the participants table and the race table. I need to add this trigger to the results table
here are my tables:
CREATE TABLE race
(
RaceID int,
Location varchar2 (60),
StartDate Date,
RaceTime Date,
MinAge int,
);
CREATE TABLE participant
(
ParticipantID int,
"Name" varchar2 (60),
DateOfBirth date,
Sex varchar2 (1),
Age int
);
CREATE TABLE results
(
RaceID int not null unique,
ParticipantID int not null unique,
position number (2)
);
here is what I have created so far:
create or replace TRIGGER dob_trg
BEFORE INSERT OR UPDATE ON RESULTS
FOR EACH ROW
DECLARE
RACE_MINAGE NUMBER;
PARTICIPANT_AGE NUMBER;
BEGIN
SELECT RACE.MINAGE, PARTICIPANT.AGE
INTO RACE_MINAGE, PARTICIPANT_AGE
FROM RACE PARTICIPANT
WHERE RACE.RACEID = :NEW.RACEID;
IF : NEW.AGE < : NEW.MINAGE THEN
RAISE_APPLICATION_ERROR(-20000, 'Participant too young!');
END IF;
enddob_trg;
However I'm getting the following error
Error(9,6): PLS-00103: Encountered the symbol ":"
Any help greatly appreciated!
Erros :
Table race - there is an extra comma at the end of you MinAge col
CREATE TABLE race
(
RaceID int,
Location varchar2 (60),
StartDate Date,
RaceTime Date,
MinAge int,
);
Inside trigger errors:
IF : NEW.AGE < : NEW.MINAGE THEN should be
IF :NEW.AGE < :NEW.MINAGE THEN
Your create trigger is on table RESULTS but the action is made on a column called AGE that belongs to the participant table .
Fix this logic before going forward.
One more error inside your trigger :
FROM RACE PARTICIPANT
add a comma between tables
Fixed trigger
-here i what i think your trigger should look like !
Compile it and give it a try
See my logic :
This query will store the values of minage using the predicate "NEW.ParticipantID"
SELECT RACE.MINAGE, PARTICIPANT.AGE
INTO RACE_MINAGE, PARTICIPANT_AGE
FROM RACE, PARTICIPANT
WHERE RACE.RACEID = :NEW.ParticipantID;
This part :- if the RACE_MINAGE is bigger or equal then
IF :NEW.AGE <= RACE_MINAGE THEN
RAISE_APPLICATION_ERROR(-20000, 'Participant too young!');
END IF;
Complete trigger
CREATE OR REPLACE TRIGGER dob_trg
BEFORE INSERT OR UPDATE ON participant
FOR EACH ROW
DECLARE
RACE_MINAGE NUMBER;
PARTICIPANT_AGE NUMBER;
BEGIN
SELECT RACE.MINAGE, PARTICIPANT.AGE
INTO RACE_MINAGE, PARTICIPANT_AGE
FROM RACE, PARTICIPANT
WHERE RACE.RACEID = :NEW.ParticipantID;
exception
when NO_DATA_FOUND then
NULL; -- or do something else you choose
IF :NEW.AGE <= RACE_MINAGE THEN
RAISE_APPLICATION_ERROR(-20000, 'Participant too young!');
END IF;
end;
IF : NEW.AGE < : NEW.MINAGE THEN
You should remove the whitespace after the : symbol.
IF :NEW.AGE < :NEW.MINAGE THEN
Also, it looks like you're missing a comma in your FROM clause:
FROM RACE PARTICIPANT
But that wouldn't make your code work yet, because you're testing the wrong fields in your IF statement. Also, you're not filtering the participantId.

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