I have created a database for an airline that currently has 3 tables: Table1: FLIGHTS, Table2: Clients, Table3: Reservation, and I want to create a function that returns the time left in the month from the reservation made till the flights date inside a function. After I run it, nothing is returning. Any tips?
DROP DATABASE if exists Chartered_Airlines;
CREATE DATABASE Chartered_Airlines;
USE Chartered_Airlines;
CREATE TABLE FLIGHTS(FLIGHT_NO INT(5) NOT NULL, DEPARTURE VARCHAR(15), ARRIVAL VARCHAR(15), TYPEFL VARCHAR(15), SEATS INT(4) NOT NULL, FREE_SEATS INT(4), FLIGHT_DATE DATE,
PRIMARY KEY(FLIGHT_NO));
CREATE TABLE CUSTOMERS(CL_NO INT(5) NOT NULL, LAST_NAME VARCHAR(15) NOT NULL, FIRST_NAME VARCHAR(15) NOT NULL, CITIZENSHIP VARCHAR(15) NOT NULL, B_DATE DATE,
PRIMARY KEY(CL_NO));
CREATE TABLE RESERVATIONS(RES_NO INT(5) NOT NULL, CL_NO INT(5) NOT NULL, FLIGHT_NO INT(5), COST FLOAT(15), RES_DATE DATE,
PRIMARY KEY(RES_NO),
FOREIGN KEY(FLIGHT_NO) REFERENCES FLIGHTS(FLIGHT_NO),
FOREIGN KEY(CL_NO) REFERENCES CUSTOMERS(CL_NO));
SHOW TABLES;
INSERT INTO FLIGHTS VALUES(10,'ATHENS','LONDON','EXTERNAL',310,34, '2023/10/04');
INSERT INTO FLIGHTS VALUES(20,'ATHENS','MYKONOS','INTERNAL',100,5, '2022/12/12');
INSERT INTO FLIGHTS VALUES(30,'ATHENS','PARIS','EXTERNAL',256,16, '2022/09/07');
INSERT INTO CUSTOMERS VALUES(5472,'ALEKSANDRAKIS','GEORGIOS','GREECE','1989/01/01');
INSERT INTO CUSTOMERS VALUES(1354,'STAVROU','ANTREAS','GREECE','1977/07/07');
INSERT INTO CUSTOMERS VALUES(6598,'AMALFIO','ROBERTO','ITALY','1995/05/02');
INSERT INTO CUSTOMERS VALUES(1953,'EUSTATHIOU','NIKOS','GREECE','2001/06/03');
INSERT INTO CUSTOMERS VALUES(1387,'STAKAS','VASILLIS','GREECE','1990/01/07');
INSERT INTO RESERVATIONS VALUES(3174,5472,10,6482, '2020/03/02');
INSERT INTO RESERVATIONS VALUES(3143,1354,10,6482, '2021/05/09');
INSERT INTO RESERVATIONS VALUES(1286,6598,20,662, '2022/10/12');
INSERT INTO RESERVATIONS VALUES(3275,1953,20,662, '2022/09/08');
INSERT INTO RESERVATIONS VALUES(7654,1387,30,264, '2022/09/06');
SELECT * FROM FLIGHTS;
SELECT * FROM CUSTOMERS;
SELECT * FROM RESERVATIONS;
DESC FLIGHTS;
DESC RESERVATIONS;
DESC CUSTOMERS;
DELIMITER //
CREATE FUNCTION TIME_LEFT(RES_NO INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT FROM RESERVATIONS
WHERE RES_NO = RES_NO AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO ;
RETURN TIME_LEFT;
END;
//
SELECT TIME_LEFT(3143);
I was expecting that it returns the date difference between reservation made date and flight date
Your forgot to use join and also use a variable name for holding the value passed in function:
DELIMITER //
CREATE FUNCTION TIME_LEFT(VAR_RES_NO INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT FROM RESERVATIONS join FLIGHTS
WHERE RES_NO = VAR_RES_NO AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO ;
RETURN TIME_LEFT;
END;
//
I think you need to include the table Flights in the SELECT statement, as well as specify the difference between RES_NO in the input vs reservations e.g.,
CREATE FUNCTION TIME_LEFT(RES_NO_in INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT
FROM RESERVATIONS, FLIGHTS
WHERE RESERVATIONS.RES_NO = RES_NO_in AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO;
RETURN TIME_LEFT;
END;
or
CREATE FUNCTION TIME_LEFT(RES_NO_in INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT
FROM RESERVATIONS
INNER JOIN FLIGHTS ON RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO
WHERE RESERVATIONS.RES_NO = RES_NO_in;
RETURN TIME_LEFT;
END;
Related
I am currently working under postgres 14.
I got two tables customers and contacts, and i want to automatically find the customer_id from the customer name when i add a new contact in the table contacts.
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
);
INSERT INTO customers(customer_name) VALUES('my_first_customer')
So from here i want to do something like that :
INSERT INTO contacts(customer_id, contact_name ) VALUES('my_first_customer',"thomas")
and i want to get this result in contacts table :
contact_id
customer_id
contact_name
1
1
"thomas"
i tried to make a function to change the value from name to id but get an error of type.
Because the the type error is catch before the trigger. Here is my function
CREATE FUNCTION get_id_fct()
RETURNS TRIGGER AS $get_id_fct$
DECLARE val_id INTEGER;
BEGIN
SELECT customer_id INTO val_id FROM customers WHERE customers.customer_id = NEW.customer_id;
NEW.customer_id := val_id;
RETURN NEW
END
$get_id_fct$ LANGUAGE plpgsql;
CREATE TRIGGER get_id
BEFORE INSERT ON contacts
FOR EACH ROW EXECUTE PROCEDURE get_id_fct();'
Is their a way around that ? or specific method to do this task that i don't know about ?
I am quite a beginner at SQL.
You can make a subquery in insert statement
INSERT INTO contacts(customer_id, contact_name ) VALUES (
(select customer_id from customers where customer_name = 'my_first_customer') ,'thomas');
This query will fail if there is more than one customer with given name, and insert a null value for customer_id if there is no customers with given name
Different approach (proposed by #wildplasser)
INSERT INTO contacts(customer_id, contact_name )
select customer_id,'thomas' from customers where customer_name = 'my_first_customer';
In this case, when there is no customer with given name, no row will be created. When there is more than one customer with given name, for each of them record will be created.
Or you can create view with INSTEAD OF trigger.
create view v_contacts as
select customer_name, contact_name from customers
join contacts on customers.customer_id = contacts.customer_id;
CREATE FUNCTION emp () RETURNS trigger AS $$
BEGIN
INSERT INTO contacts(customer_id, contact_name )
VALUES((select customer_id from customers where customer_name =
NEW.customer_NAME),NEW.contact_NAME);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER v_contactst
INSTEAD OF INSERT ON v_contacts
FOR EACH ROW
EXECUTE FUNCTION emp();
Insert statement
INSERT INTO v_contacts(customer_name, contact_name ) VALUES('my_first_customer','thomas')
And just a note, that columns customer_name and contact_name are not unique so this code is not safe and throw error if customer_name didn't exists or there is more than one record with this customer_name
(zaposlenik means emplpoyee, godisnji_odmor means vacation)
So I want to make a trigger that triggers if an employee(here is called zaposlenik) has more than 21 days of vacation after an insert in year 2020. An employee can have multiple vacations(like 2 days, 10 days...) one to many relation between tables zaposlenik(employee) and godisnji_odmor(vacation). My problem here is that I don't know how to get that zaposlenik_id from the insert on which the trigger tiggers so I can sum up the vacation days of that employee on the ID = zaposlenik_id.
Here are my tables and trigger.
CREATE TABLE zaposlenik
(
zaposlenik_id INTEGER CONSTRAINT zaposlenik_pk PRIMARY KEY,
posao VARCHAR(30) NOT NULL,
ime VARCHAR(30) NOT NULL,
prezime VARCHAR(30) NOT NULL,
broj_tel INTEGER NOT NULL,
email VARCHAR(50) NOT NULL,
adresa VARCHAR(100) NOT NULL,
mjesecni_iznos_place FLOAT NOT NULL,
IBAN VARCHAR(34) NOT NULL,
budzet FLOAT,
parking_mjesto_id VARCHAR(5) CONSTRAINT zaposlenik_parking_mjesto_fk REFERENCES
parking_mjesto(etaza_i_br_mjesta),
zaposlenik_id_2 INTEGER CONSTRAINT zaposlenik_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE TABLE godisnji_odmor
(
godisnji_odmor_id INTEGER CONSTRAINT godisnji_odmor_pk PRIMARY KEY,
pocetak DATE NOT NULL,
kraj DATE NOT NULL,
zaposlenik_id INTEGER NOT NULL CONSTRAINT godisnji_odmor_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020' AND zaposlenik_id = v_id;
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/
You need to use the :new for this as follows:
replace
SELECT zaposlenik_id INTO v_id FROM INSERTED;
with
v_id := :new.zaposlenik_id;
In short, You can have your code without v_id variable as follows:
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
--v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
--SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020'
AND zaposlenik_id = :new.zaposlenik_id; -- see the usage of :new here
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/
CREATE TABLE projeto.Person (
Person_Code INT IDENTITY(1,1) NOT NULL,
birthDate DATE NOT NULL,
Name VARCHAR(50) NOT NULL,
PRIMARY KEY (Person_Code)
)
CREATE TABLE projeto.Student (
Student_Code INT REFERENCES projeto.Person (Person_Code),
payment INT NOT NULL,
PRIMARY KEY (Student_Code),
)
CREATE TABLE projeto.teacher (
payment INT NOT NULL,
teacher_Code INT,
PRIMARY KEY (teacher_Code),
CHECK (payment > 350)
)
How do I insert values in student, paying attention that a student has all person attributes? e.g. student has name, birth_date etc.
I tried this:
INSERT INTO projeto.Person VALUES
('1961-03-26', John Adam')
but this only adds in a person, and I can't tell if its a student or not.
I guess its how to get the recently inserted Person_Code that you are asking? In which case use scope_identity().
declare #BirthDate date, #Name varchar(50), #Payment int, #IsStudent bit, #IsTeacher bit, #NewPersonCode int;
-- SET THE RELEVANT VARIABLE VALUES
-- Insert person
insert into projeto.Person (BirthDate, [Name])
select #BirthDate, #Name;
-- Get the Person_Code of the new person record
set #NewPersonCode = scope_identity();
-- Insert Student record if a student
insert into projeto.Student (Student_Code, Payment)
select #NewPersonCode, #Payment
where #IsStudent = 1;
-- Insert Teacher record if a teacher
insert into projeto.Teacher (Teacher_Code, Payment)
select #NewPersonCode, #Payment
where #IsTeacher = 1;
I want a list of the 5 toys that have cost more money, in descending order if we have delivering each toy every single time.
For example, I have an Ipad that cost 600€ and is requested by child 1, 2 and 3. Now, I have a Nintendo that cost 300€ and is requested 3 times (1 time by child 1 and 2 times by the child 3). I have another toy (Laptop) that cost 360€ and is requested 8 times (2 times by child 1 and 6 times by child 3). Then, I must see:
Laptop because is requested 8 times and cost 360€ (in total 8 * 360€ = 2880€)
Ipad because is requested 3 times and cost 600€ (in total 3 * 600€ = 1800€)
Nintendo
When I have this, I want to see the child's data that more times request the same toy, for example, in case of Nintendo I would like see information about child 2. In case of laptop I would like see information about child 3.
I create this type:
CREATE TYPE ToysList AS (
t_Toy_name VARCHAR(255),
t_Price REAL,
t_Times_requested INTEGER,
t_Total_amount_money REAL,
t_Child_name VARCHAR(255),
t_Child_times_request SMALLINT,
t_Child_address VARCHAR(255),
t_Number_Siblings SMALLINT);
The tables are these:
CREATE TABLE CHILD(
child_id SMALLINT,
child_name VARCHAR(255) NOT NULL,
birth_date DATE NOT NULL,
gender VARCHAR(255) NOT NULL,
address VARCHAR(255),
city VARCHAR(255),
CONSTRAINT PK_CHILD PRIMARY KEY(child_id),
CONSTRAINT VALID_GENDER CHECK (gender IN ('m', 'f')),
CONSTRAINT VALID_DATE CHECK (birth_date <= now())
);
CREATE TABLE letter (
letter_id SMALLINT NOT NULL,
arrival_date DATE DEFAULT now() NOT NULL,
number_toys INTEGER NOT NULL,
child_id SMALLINT,
CONSTRAINT valid_child_id CHECK ((child_id IS NOT NULL)),
CONSTRAINT PK_LETTER PRIMARY KEY(letter_id),
CONSTRAINT CHILD_FK FOREIGN KEY (child_id) REFERENCES CHILD(child_id)
);
CREATE TABLE SIBLING(
child_id1 SMALLINT,
child_id2 SMALLINT,
CONSTRAINT PK_SIBLING PRIMARY KEY(child_id1, child_id2),
CONSTRAINT CHILD1_FK FOREIGN KEY (child_id1) REFERENCES CHILD(child_id),
CONSTRAINT CHILD2_FK FOREIGN KEY (child_id2) REFERENCES CHILD(child_id)
);
CREATE TABLE TOY(
toy_id SMALLINT,
toy_name VARCHAR(255) NOT NULL,
price REAL NOT NULL,
toy_type VARCHAR(255) NOT NULL,
manufacturer VARCHAR(255),
CONSTRAINT PK_TOY PRIMARY KEY(toy_id),
CONSTRAINT POSITIVE_PRICE CHECK (price > 0),
CONSTRAINT VALID_TYPE CHECK(toy_type IN ('symbolic', 'rule', 'educational', 'cooperative', 'other'))
);
CREATE TABLE WISHED_TOY(
letter_id SMALLINT,
toy_id SMALLINT,
CONSTRAINT PK_WISHED_TOY PRIMARY KEY(letter_id, toy_id),
CONSTRAINT LETTER_FK FOREIGN KEY (letter_id) REFERENCES LETTER(letter_id),
CONSTRAINT TOY_FK FOREIGN KEY (toy_id) REFERENCES TOY(toy_id)
);
At this moment I did this:
CREATE OR REPLACE FUNCTION list_top_Toys() RETURNS SETOF ToysList AS $$
DECLARE
l_Toy_name VARCHAR(255);
l_Price REAL;
l_Times_requested INTEGER;--total times requested for this toy
l_Total_amount_money REAL; --total times requested * price toy
l_Child_name VARCHAR(255);
l_Child_times_request SMALLINT; --times request for the child
l_Child_address VARCHAR(255);
l_Number_Siblings SMALLINT;
l_toy_id INTEGER;
l_child_id INTEGER;
l_letter_id INTEGER;
returnset ToysList;
BEGIN
FOR l_toy_id, l_Toy_name, l_Times_requested, l_Total_amount_money
IN SELECT t.toy_id, t.toy_name, COUNT(*), SUM(price) AS totalAmountMoney
FROM toy t INNER JOIN wished_toy WT ON t.toy_id = WT.toy_id
GROUP BY t.toy_id, t.toy_name
ORDER BY totalAmountMoney DESC, t.toy_name
LIMIT 5
LOOP
returnset.t_Toy_name = l_Toy_name;
returnset.t_Times_requested = l_Times_requested;
returnset.t_Total_amount_money = l_Total_amount_money;
SELECT c.child_id, c.child_name, c.address, SUM(L.number_toys) AS totalToys
INTO l_child_id, l_Child_name, l_Child_address, l_Child_times_request
FROM child c
INNER JOIN letter L ON c.child_id = L.child_id
INNER JOIN wished_toy WIS ON WIS.letter_id = L.letter_id
WHERE c.child_id = l_child_id
GROUP BY c.child_id, c.child_name
ORDER BY totalToys DESC
LIMIT 1;
returnset.t_Child_name = l_Child_name;
returnset.t_Child_address = l_Child_address;
returnset.t_Child_times_request = l_Child_times_request;
SELECT COUNT(s.child_id2) AS numberSiblings
INTO l_Number_Siblings
FROM sibling s
INNER JOIN child c1 ON c1.child_id = s.child_id1
WHERE s.child_id1 = l_child_id
LIMIT 1;
returnset.t_Number_Siblings = l_Number_Siblings;
return next returnset;
END LOOP;
END;
$$LANGUAGE plpgsql;
COMMIT;
Can anyone say me what I am doing wrong?
Thank you,
Your function returns setof type of data. So, just select from it`s result, like this:
select * from list_top_Toys();
After that You can manipulate with results as it is table.
But, as I can see, this function needs much more changes.
Second query gives same results in every LOOP iteration, so I changed it to reflect result of first SELECT, and to make Letters table as leading in query.
At first, why group by toy_name - there need to be only group by toy_id.
Also, group by child_name (in first inner query) is redundant.
I would include toy_id in result set, it may be useful in later computations.
Also, You did not set toy price as you says in your post, so first SELECT have to have toy`s price.
So, my version of your function will be:
CREATE OR REPLACE FUNCTION list_top_Toys()
RETURNS SETOF ToysList
AS $$
DECLARE
l_Toy_name VARCHAR(255);
l_Price REAL;
l_Times_requested INTEGER;--total times requested for this toy
l_Total_amount_money REAL; --total times requested * price toy
l_Child_name VARCHAR(255);
l_Child_times_request SMALLINT; --times request for the child
l_Child_address VARCHAR(255);
l_Number_Siblings SMALLINT;
l_toy_id INTEGER;
l_child_id INTEGER;
l_letter_id INTEGER;
returnset ToysList;
BEGIN
FOR l_toy_id, l_Toy_name, l_Price, l_Times_requested, l_Total_amount_money
IN SELECT t.toy_id, t.toy_name, t.price, COUNT(*), SUM(price) AS totalAmountMoney
FROM toy t
INNER JOIN wished_toy WT ON t.toy_id = WT.toy_id
GROUP BY t.toy_id
ORDER BY totalAmountMoney DESC, t.toy_name
LIMIT 5
LOOP
returnset.t_Toy_name = l_Toy_name;
returnset.t_Price = l_price;
returnset.t_Times_requested = l_Times_requested;
returnset.t_Total_amount_money = l_Total_amount_money;
SELECT c.child_id, c.child_name, c.address, SUM(L.number_toys) AS totalToys
INTO l_child_id, l_Child_name, l_Child_address, l_Child_times_request
FROM letter L
INNER JOIN child c ON c.child_id = L.child_id
INNER JOIN wished_toy WIS ON WIS.letter_id = L.letter_id
WHERE wis.toy_id = l_toy_id
GROUP BY c.child_id, c.child_name
ORDER BY totalToys DESC
LIMIT 1;
returnset.t_Child_name = l_Child_name;
returnset.t_Child_address = l_Child_address;
returnset.t_Child_times_request = l_Child_times_request;
SELECT COUNT(s.child_id2) AS numberSiblings
INTO l_Number_Siblings
FROM sibling s
INNER JOIN child c1 ON c1.child_id = s.child_id1
WHERE s.child_id1 = l_child_id
LIMIT 1;
returnset.t_Number_Siblings = l_Number_Siblings;
return next returnset;
END LOOP;
END;
$$LANGUAGE plpgsql;
I did not touch siblings query.
I am implementing a library management system in SQL. I have the following table structure and some values inserted in them:
create table books
(
IdBook number(5),
NameBook varchar2(35),
primary key(IdBook)
);
create table users
(
IdUsers number(5),
NameUser varchar2(20),
primary key(IdUsers)
);
create table borrowed
(
IdBorrowed number(5),
IdUsers number(5),
IdBook number(5),
DueDate date,
DateReturned date,
constraint fk_borrowed foreign key(IdUsers) references users(IdUsers),
constraint fk_borrowed2 foreign key(IdBook) references books(IdBook)
);
insert into books values(0,'FairyTale');
insert into books values(1,'Crime and Punishment');
insert into books values(2,'Anna Karenina');
insert into books values(3,'Norwegian Wood');
insert into users values(01,'Robb Dora');
insert into users values(02,'Pop Alina');
insert into users values(03,'Grozavescu Teodor');
insert into users values(04,'Popa Alin');
insert into borrowed values(10,02,3,'22-Jan-2017',null);
insert into borrowed values(11,01,1,'25-Jan-2017','19-Dec-2016');
insert into borrowed values(12,01,3,'22-Jan-2017',null);
insert into borrowed values(13,04,2,'22-Jan-2017','13-Dec-2016');
What I want now is that my db to allow "borrowing" books for the users(i.e insert into the borrowed table) that have no unreturned books(i.e date returned is not null) and if they have unreturned books I want to abandon the whole process. I thought to implement this in the following way:
create or replace procedure borrowBook(IdBorrowed in number,IdUsers number,IdBook number,DueDate date,DateReturned date) as begin
if exists (SELECT u.IdUsers, u.NameUser, b.DateReturned
FROM users u, borrowed b
WHERE u.IDUSERS = b.IdUsers and DateReturned is not null),
insert into borrowed values(IdBorrowed,IdUsers,IdBook,DueDate,DateReturned);
end borrowBook;
The above procedure does not check if the parameter I pass to this function is the same as the one in my select and I do not know how to do this and correctly insert a value in my table.
Any help would be much appreciated. Thank in advance!
You should not name your parameters the same as columns also used inside the procedure.
You can also simplify your procedure to a single INSERT statement, no IF required:
create or replace procedure borrowBook(p_idborrowed in number, p_idusers number, p_idbook number, p_duedate date, p_datereturned date)
as
begin
insert into borrowed (idborrowed, idusers, idbook, duedate, datereturned)
select p_idborrowed, p_idusers, p_idbook, p_duedate, p_datereturned
from dual
where not exists (select *
from users u
join borrowed b on u.idusers = b.idusers
and b.datereturned is not null);
end borrowBook;
It's also good coding style to explicitly list the columns for an INSERT statement. And you should get used to the explicit JOIN operator instead of using implicit joins in the where clause.
What about this one:
create or replace procedure borrowBook( p_IdBorrowed in number ,
p_IdUsers number ,
p_IdBook number ,
p_DueDate date ,
p_DateReturned date )
as
begin
if (SELECT COUNT(*)
FROM borrowed
WHERE IDUSERS = p_IdUsers
AND DateReturned IS NULL) = 0 THEN
insert into borrowed values (p_IdBorrowed ,
p_IdUsers ,
p_IdBook ,
p_DueDate ,
p_DateReturned );
end if ;
end borrowBook;
You would seem to want something like this:
create or replace procedure borrowBook (
in_IdBorrowed in number,
in_IdUsers number,
in_IdBook number,
in_DueDate date,
in_DateReturned date
) as
v_flag number;
begin
select (case when exists (select 1
from borrowed b
where b.IdUsers = in_IdUsers and b.DateReturned is not null
)
then 1 else 0
end)
into v_flag
from dual;
if (flag = 0) then
insert into borrowed
values(in_IdBorrowed, in_IdUsers, in_IdBook, in_DueDate, v_DateReturned);
end if
end -- borrowBook;