SQL Check with values from multiple tables - sql

If I have 2 tables like this:
Authors(
book char(30) PRIMARY KEY,
earnings INT
);
Books(
book char(30),
sales INT
FOREIGN KEY(book) REFERENCES Authors(book)
);
How would I write a check to enforce earnings for a book being less than sales for the book in Oracle SQL and where would I put it since it uses values from both tables?

If my understanding of your condition "check to enforce earnings for a book to be less than sales for the book" is correct, the primary and foreign key creation in tables need to be repositioned as following:
Rem -- Books Table
create table books(
book char(30) primary key,
sales int
);
Rem -- Authors Table
create table authors (
book char(30),
earnings int,
foreign key(book) references books(book)
);
Rem -- Create a trigger to enforce the condition
Rem -- Earnings for a book should be less than sales
CREATE OR REPLACE TRIGGER check_earnings BEFORE
INSERT ON authors FOR EACH ROW
DECLARE
earnings_higher_than_sales EXCEPTION;
sales_val int;
BEGIN
select sales into sales_val from books where book=:new.book;
dbms_output.put_line('Corresponding sales value:'||sales_val);
IF :new.earnings > sales_val THEN
RAISE earnings_higher_than_sales;
END IF;
EXCEPTION
WHEN earnings_higher_than_sales THEN
RAISE_APPLICATION_ERROR(-20000, 'Earnings values can''t be greater than sales');
END;
/
Rem -- Insert some rows into books
insert into books values('davinci code',100);
insert into books values('alchemist',200);
insert into books values('monk who sold his ferrari',300);
insert into books values('digital fortress',400);
insert into books values('unbowed',500);
commit;
Rem -- Insert some rows into authors
Rem -- Following two will succeed
insert into authors values('davinci code',90);
insert into authors values('alchemist',180);
Rem -- Following will fail, as the earnings(320) is greater than sales(300)
insert into authors values('monk who sold his ferrari',320);
Rem -- Following will succeed
insert into authors values('monk who sold his ferrari',290);
Rem -- Following two will succeed
insert into authors values('digital fortress',340);
insert into authors values('unbowed',400);
commit;
So, the answer is to create trigger on before insert or update on table Authors.

Related

sql oracle - constraint on 2 columns from different tables

I have designed a ticket system booking for flights. I want to add a constraint such that the number of tickets you can insert to be less than number of seats from a flight plane.
Let's say I inserted a flight with a plane with 10 seats. I can insert only 10 tickets for that particular flight. Otherwise, an error message should appear.
I tried to make a trigger using the count function on flight number.
CREATE OR REPLACE TRIGGER trg_ticket_BRIU
BEFORE INSERT OR UPDATE ON Ticket
FOR EACH ROW
DECLARE
l_numberofseats flight.numberofseats%type;
BEGIN
select numberofseats into l_numberofseats
from flight
where flightnumber=:new.flightnumber;
IF :new.count(flightnumber) > l_numberofseats
THEN
raise_application_error(-2000, 'Not enough seats');
END IF;
END;
but I get this error
Trigger TRG_TICKET_BRIU compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
8/5 PLS-00049: bad bind variable 'NEW.COUNT'
Errors: check compiler log
Personally, I would add an AIRCRAFT and a SEAT table:
CREATE TABLE aircraft (
id NUMBER
GENERATED ALWAYS AS IDENTITY
CONSTRAINT aircraft__id__pk PRIMARY KEY,
tail_number VARCHAR2(6)
NOT NULL
CONSTRAINT aircraft__tn__u UNIQUE
CONSTRAINT aircraft__tn_chk CHECK(
REGEXP_LIKE(
tail_number,
'[A-Z]\d{1,5}|[A-Z]\d{1,4}[A-Z]|[A-Z]\d{1,3}[A-Z]{2}'
)
),
manufacturer VARCHAR2(20)
NOT NULL,
model VARCHAR2(20)
NOT NULL,
airline_id CONSTRAINT aircraft__aid__fk REFERENCES airline(airline_id)
NOT NULL
);
CREATE TABLE seat (
id NUMBER
GENERATED ALWAYS AS IDENTITY
CONSTRAINT seat__id__pk PRIMARY KEY,
aircraft_id CONSTRAINT seat__aid__fk REFERENCES aircraft(id)
NOT NULL,
seat_row VARCHAR2(3)
NOT NULL,
seat_column NUMBER
NOT NULL,
CONSTRAINT seat__aid_r_c__u UNIQUE (aircraft_id, seat_row, seat_column)
);
Then your flight table would reference the aircraft:
CREATE TABLE flight (
id NUMBER
GENERATED ALWAYS AS IDENTITY
CONSTRAINT flight__id__pk PRIMARY KEY,
aircraft_id CONSTRAINT flight__aid__fk REFERENCES aircraft(id)
NOT NULL
-- ...
);
And the ticket would reference a flight and a seat:
CREATE TABLE ticket (
id NUMBER
GENERATED ALWAYS AS IDENTITY
CONSTRAINT ticket__id__pk PRIMARY KEY,
flight_id CONSTRAINT ticket__fid__fk REFERENCES flight(id)
NOT NULL,
seat_id CONSTRAINT ticket__sid__fk REFERENCES seat(id)
NOT NULL,
-- ...
CONSTRAINT ticket__fid_sid__u UNIQUE (flight_id, seat_id)
);
Then you can never sell a seat that does not exist on an aircraft and do not need to count the maximum number of tickets and compare it to seats (and the seat has added attributes like its location on the plane that can be displayed on the ticket).
All you need then is to ensure the referential consistency that, for a ticket, the flight and the seat are on the same aircraft; which can be done with a trigger:
CREATE TRIGGER ticket_check_seat_on_flight
BEFORE INSERT OR UPDATE ON ticket
FOR EACH ROW
DECLARE
is_valid NUMBER(1);
BEGIN
SELECT 1
INTO is_valid
FROM flight f
INNER JOIN seat s
ON (f.aircraft_id = s.aircraft_id)
WHERE f.id = :NEW.flight_id
AND s.id = :NEW.seat_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(
-20000,
'Flight and seat are on different aircraft.'
);
END;
/
db<>fiddle here
You can use an AFTER STATEMENT trigger:
CREATE TRIGGER ticket__check_number_of_seats
AFTER INSERT OR UPDATE OR DELETE ON ticket
DECLARE
is_invalid NUMBER(1,0);
BEGIN
SELECT 1
INTO is_invalid
FROM flight f
INNER JOIN (
SELECT flight_id,
COUNT(*) AS tickets_sold
FROM ticket
GROUP BY flight_id
) t
ON f.id = t.flight_id
WHERE t.tickets_sold > f.number_of_seats;
RAISE_APPLICATION_ERROR(
-20000,
'Too many tickets sold for flight.'
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
/
It could be made more efficient by using a compound trigger to collate, for each row, the flight_id values into a collection and then, after the statement, only checking the number of tickets for those flights; however, I'll leave that extension as an exercise for the OP.
db<>fiddle here
As others indicated there is no :new.count column. This is because :new (and :old) create a pseudo-row containing exactly the same columns as the table definition. Further you will get a Mutating exception as what you need to count in the flight_number from tickets. However, since that is the table causing he trigger to fire you cannot reference it. So what to do: create a compound trigger, and a supporting Type (nested table). Within it use the after row section to capture the flight_numbers processed. Then in the after statement section you can select count of tickets for each flight. If that count > 0 then raise your exception. ( see Demo )
create type flight_tickets_ntt
is table of integer;
create or replace trigger trg_ticket_ciu
for update or insert on tickets
compound trigger
l_flights flight_tickets_ntt := flight_tickets_ntt();
after each row is
begin
if :new.flight_number not member of l_flights then
l_flights.extend ;
l_flights(l_flights.count) := :new.flight_number;
end if;
end after each row;
after statement is
l_flight_cnt flight.flight_number%type;
begin
select count(*)
into l_flight_cnt
from flight f
where f.number_of_seats <
( select count(*)
from tickets t
where t.flight_number in
( select *
from table (l_flights)
)
);
if l_flight_cnt > 0 then
raise_application_error(-20000, 'Not enough seats');
end if;
end after statement;
end trg_ticket_ciu;
There remains a you need to handle: What happens if an update changes the flight number or perhaps (missing column) the data of the flight.

Triggers working correctly, but raise application error causing some errors with insert statements

I have my code shown below. Everything is working correctly but when my insert statements violate the trigger it throws errors because of my raise_application_error.
--Drop tables in case they were created previously
DROP TABLE enrollment Purge;
DROP TABLE offering Purge;
DROP TABLE faculty Purge;
DROP TABLE course Purge;
DROP TABLE student Purge;
--Create student table
CREATE TABLE student(StdID number(3) constraint student_StdID_PK primary key,
StdFN varchar2(10),
StdLN varchar2(15),
StdCity varchar2(15),
StdState char(2),
StdZip number(5),
StdMajor varchar2(4),
StdClass char(2),
StdGPA number(2,1),
StdBalance NUMBER (12,2));
--create a table for courses and their descriptions
CREATE TABLE course(CourseNo varchar2(8) constraint course_courseNo_PK primary key,
CrsDesc varchar2(40),
CrsCredits number(1));
--create faculty table
CREATE TABLE Faculty(FacID number(4) constraint faculty_FacID_PK primary key,
FacFN varchar2(10),
FacLN varchar2(15),
FacDept varchar2(4),
FacRank varchar2(4),
FacHireDate date,
FacSalary number(6),
FacSupervisor number(4));
--create table for offered courses
CREATE TABLE Offering(OfferNo number(4) constraint offering_OfferNo_PK primary key,
CourseNo varchar(8)constraint offering_crs_no references course(courseno),
OffTerm varchar2(6),
OffYear number(4),
OffLoca varchar(6),
OffTime varchar(8),
OffDay varchar(5),
FacSSN number(4)constraint offering_fac_FK references faculty(facID));
--create table for student enrollment
CREATE TABLE enrollment(StdID number(3),
OfferNo number(4),
EnrGrade char(2),
constraint enrollment_PK primary key (StdID,OfferNo),
constraint enrollment_std_ID foreign key(StdID) references student(stdID),
constraint enrollment_class_ID foreign key(offerno) references offering(offerno));
CREATE OR REPLACE TRIGGER UNPAID_BALANCE
BEFORE INSERT ON STUDENT
FOR EACH ROW
DECLARE
BAL NUMBER;
BEGIN
BAL := :NEW.STDBALANCE;
IF BAL > 500
THEN
RAISE_APPLICATION_ERROR(-20001, 'Your balance is too high to register. Please pay to continue.');
END IF;
END;
/
CREATE OR REPLACE TRIGGER ENROLL_MAX
BEFORE INSERT ON ENROLLMENT
FOR EACH ROW
DECLARE
NOFSTUDENTS BINARY_INTEGER;
BEGIN
SELECT COUNT(*) INTO NOFSTUDENTS
FROM ENROLLMENT
WHERE OFFERNO = :NEW.OFFERNO AND ENRGRADE IS NULL;
DBMS_OUTPUT.PUT_LINE(NOFSTUDENTS);
IF (NOFSTUDENTS + 1) > 2
THEN
RAISE_APPLICATION_ERROR(-20003, 'EXCEED MAX NO OF STUDENTS ALLOWED');
END IF;
END;
/
--populate student table
INSERT INTO student values(101,'Joe','Smith','Eau Clare','WI',18121,'IS','FR',3.8,225.25);
INSERT INTO student values(102, 'Rob','King', 'Melrose', 'MN', 56352, 'IS','JR',3.2,120.98);
INSERT INTO student values(103, 'Dan','Robinson', 'Sartell', 'MN', 98042, 'IS','JR',3.6, 36);
INSERT INTO student values(104,'Sue','Williams','St.Cloud','MN',56301,'ACCT','SR',3.2,2386.55);
INSERT INTO student values(105,'Don','Robinson','St.Paul','MN',55103,'MKTG','SR',3.4, 306);
--populate course table
INSERT INTO Course values('CSCI 200','Elements of Computing',3);
INSERT INTO Course values('IS 250','Application of Program Dev. I',3);
INSERT INTO Course values('IS 251','Application of Program Dev. II',3);
INSERT INTO Course values('IS 454', 'Data Mining for Decision Support',3);
INSERT INTO Course values('IS 356','Systems Analysis and Design I',3);
INSERT INTO Course values('IS 460', 'Project Management',3);
INSERT INTO Course Values('ACCT 291','Accounting Principles II',3);
INSERT INTO Course values('IS 443','Database Design',3);
--populate faculty table
INSERT INTO faculty values(9001,'Leonard','Vince','IS','ASST','12-Apr-1997',67000,9003);
INSERT INTO faculty values(9002,'Victor','Strong','CSCI','ASSO','8-Aug-1999',70000,9003);
INSERT INTO faculty values(9003,'Nicki','Colan','IS','PROF','20-Aug-1981',75000,9010);
INSERT INTO faculty values(9004,'Fred','Wells','ACCT','ASST','28-Aug-1996',60000,9010);
INSERT INTO faculty values(9010,'Chris','Macon','ACCT','ASST','4-Aug-1980',75000,Null);
--populate offering table
INSERT INTO offering values(2201,'CSCI 200','Spring',2017,'ECC135','10:30AM','MWF',9002);
INSERT INTO offering values(2202,'CSCI 200','Spring',2017,'ECC135','8:00AM','MWF',9002);
INSERT INTO offering values(1102,'ACCT 291','Spring',2017,'CH 14A','2:00AM','MWF',9004);
INSERT INTO offering values(2203,'IS 356','Fall',2017,'CH494','3:30AM','TTH',9001);
INSERT INTO offering values(2204,'IS 251','Fall',2017,'CH494','12:30AM','TTH',9003);
INSERT INTO offering values(1101,'ACCT 291','Fall',2017,'CH350','12:30AM','TTH',9010);
INSERT INTO offering values(2205,'IS 443','Fall',2017,'CH494','9:30AM','MWF',9003);
--populate enrollment table
INSERT INTO enrollment values(101,2201,'A');
INSERT INTO enrollment values(102,2202,'B');
INSERT INTO enrollment values(102,2203,null);
INSERT INTO enrollment values(103,2203,null);
INSERT INTO enrollment values(103,2201,'C');
INSERT INTO enrollment values(103,1101,null);
INSERT INTO enrollment values(104,2202,'A');
INSERT INTO enrollment values(101,2203,null);
INSERT INTO enrollment values(101,1101,null);
INSERT INTO enrollment values(101,2205,null);
INSERT INTO enrollment values(102,2205,null);
INSERT INTO enrollment values(104,2205,null);
Is there a way I can make this look cleaner/better in the script output section because one specific case is this error message that pops up because of the raise application error.
Error starting at line : 129 in command -
INSERT INTO enrollment values(104,2202,'A')
Error report -
ORA-02291: integrity constraint (ADMIN_BF.ENROLLMENT_STD_ID) violated - parent key not found
The error that you are facing is clear enough. There is not student 104 in the table, so you cannot create a dependent record in enrollment. This error is not related to your triggers, but to your data.
Now let's look at the insert statement for that particular user:
INSERT INTO student values(104,'Sue','Williams','St.Cloud','MN',56301,'ACCT','SR',3.2,2386.55);
You are attempting to input a balance of 2386.55. But your trigger UNPAID_BALANCE prevents balances that are above 500, so that insert raises application error "Your balance is too high to register", which is the root cause of the problem. You need to fix that insert.
Side note: there is another problem lurking in the last series of inserts into enrollments. Trigger ENROLL_MAX allows only two students per OFFERNO, but you are attempting to assign three students to offer 2203, and three as well to offer 2205. This will raise application error "EXCEED MAX NO OF STUDENTS ALLOWED".

Unable to create or query tables in online sql interpreter

I've been trying to simply create and display information from these tables on an interpreter that uses sql.js to run.
I've looked through assignment forums and tried to assign primary keys in varying formats based on what was provided on w3schools and also tried to explicitly create a database to put the tables into. no changes.
DROP TABLE IF EXISTS employees;
CREATE TABLE pledges( donorId integer , donor text,
pledge integer, AmountPaid integer,
);
INSERT INTO pledges VALUES (1,'JOHNSON',6,30);
INSERT INTO pledges VALUES (2,'ROGERS',5,100);
INSERT INTO pledges VALUES (1,'RODDUCK',10,50);
INSERT INTO pledges VALUES (1,'PETERS',2,20);
INSERT INTO pledges VALUES (1,'ALBERTSON',7,56);
SELECT * FROM pledges;
The expectation is just for me to create the simple tables and test the queries but it just keeps saying "fetching results"
Your online tool probably swallows the error due to the incorrect CREATE TABLE statement. You have a dangling , after the amountpaid integer definition:
Once that error is fixed (and the pledges table is dropped instead of the employees table), your script runs fine:
DROP TABLE IF EXISTS pledges;
CREATE TABLE pledges
(
donorId integer,
donor text,
pledge integer,
amountpaid integer --<< you had a comma here
);
INSERT INTO pledges VALUES (1,'JOHNSON',6,30);
INSERT INTO pledges VALUES (2,'ROGERS',5,100);
INSERT INTO pledges VALUES (1,'RODDUCK',10,50);
INSERT INTO pledges VALUES (1,'PETERS',2,20);
INSERT INTO pledges VALUES (1,'ALBERTSON',7,56);
SELECT *
FROM pledges;
https://rextester.com/BJJGC94351

Oracle - Insert Stored Procedure Foreign Key

Instructions:
Create two tables, named employees and departments. Preface the table names with your initials. Link the two tables (foreign key) by a column called dept. Make up a few column names for each table.
Employees Table:
create table bsemployees(
dept number primary key,
empName varchar2(20),
salary number
);
Departments Table:
create table bsdepartments(
dept number references bsemployees(dept),
deptName varchar2(20)
);
Write the following stored procedures:
• Insert a row into the employees table. If the department does not exist. Insert it into the departments table.
create or replace procedure sp_employees(
a_dept IN number,
a_empName IN varchar2,
a_salary IN number
)
as
vCount number;
BEGIN
sp_check_dept(a_dept,vCount);
insert into bsemployees values(a_dept, a_empName, a_salary);
if vCount = 0 then
dbms_output.put_line('**DEPT DOES NOT EXIST**');
insert into bsdepartments (dept, deptName) values(a_dept, NULL);
end if;
END;
/
create or replace procedure sp_check_dept(
a_dept IN number,
vCount OUT number
)
as
BEGIN
select count(*)
into vCount
from bsdepartments
where dept = a_dept;
end;
/
• Insert a row into the departments table.
create or replace procedure sp_departments(
a_dept IN number,
a_deptName IN varchar2
)
as
BEGIN
insert into bsdepartments values(a_dept, a_deptName);
END;
/
I've got it pretty much all down for this assignment except for the fact that when I try to insert a row into the departments table I am getting a integrity constraint - parent key not found error.
If I do execute sp_employees(5, 'John Doe', 90000); It will display ***DEPT DOES NOT EXIST*** and will go ahead and insert the data into bsemployees and insert the dept# into bsdepartments and the deptName will be left blank based on my if-then statement. Doing a select(*) shows me this.
However if I go ahead and do execute sp_departments(1, 'human resources'); to place a row into departments I get the parent key error. I understand that I am trying to insert something that has no parent key but I do not know how to fix it.
Your table design isn't quite correct - the dept primary key needs to be added as a foreign key to employee (not as the primary key), and employee should have its own primary key:
create table bsdepartments(
dept number primary key,
deptName varchar2(20)
);
create table bsemployees(
empName varchar2(20) primary key,
dept number references bsdepartments(dept),
salary number
);
You can then do the 'add if not exists' logic in the check_dept proc:
create or replace procedure sp_check_dept(
a_dept IN number
)
as
vCount number
BEGIN
select count(*)
into vCount
from bsdepartments
where dept = a_dept;
if (vCount = 0) then
dbms_output.put_line('**DEPT DOES NOT EXIST**');
insert into bsdepartments (dept, deptName) values(a_dept, NULL);
end if;
end;
Which then simplifies the employee insertion proc as it should be guaranteed of a department:
create or replace procedure sp_insertEmployee(
a_dept IN number,
a_empName IN varchar2,
a_salary IN number
)
as
BEGIN
sp_check_dept(a_dept);
insert into bsemployees values(a_dept, a_empName, a_salary);
END
Notes
Recommend that you name the procs in alignment with their purpose, e.g. insertEmployee vs just employees
As you've noted, the problem with the 'add if not exists' approach is that you do not have sufficient data to completely populate the department table, hence the null column (but this is what your lecturer asked for)
Your realation to table department is bad. It should linked as below
create table bsdepartments(
dept number primary key,
deptName varchar2(20)
);
and it should be linked to employee table
create table bsemployees(
dept number references bsdepartments(dept),
empName varchar2(20),
salary number
);
Then if you try inserting execute sp_departments(1, 'human resources'); it will execute
an then you have to insert the employee.
Here the employee is related to department not the department is related to employee.

How do i specify an if statement within a trigger?

i need to insert an "exam entry" row into a table.
But the exam entry cannot be inserted if a student (recognised by the student number sno) is already entered into that exam (recognised by the exam code excode), and the entry also cannot be inserted if the student has more than one exam on the same day (i have an exam table holding the information abotu the exam dates).
I am fairly certain that i should be using an insert trigger function for this and have been looking at:
Example 39-3 from http://www.postgresql.org/docs/9.2/static/plpgsql-trigger.html
so far i have:
INSERT INTO
entry(excode, sno, egrade) VALUES (2, 1, 98.56)
CREATE FUNCTION entry_insert() RETURNS trigger AS $entry_insert$
BEGIN
--Check student is not entered into same exam twice
IF BLA BLA
RAISE EXCEPTION 'A student cannot be be entered into the same exam more than once';
END IF;
--Check student not taking more than one exam on same day
IF BLA BLA
RAISE EXCEPTION 'A student cannot take more than one exam on the same day';
END IF;
END;
$entry_insert$ LANGUAGE PLPGSQL;
CREATE TRIGGER entry_insert BEFORE INSERT ON entry
FOR EACH ROW EXECUTE PROCEDURE entry_insert();
the places where I've put bla bla is where i need the conditions that i cant quite figure out how to meet my conditions.
would love some help?
edit: my exam table
CREATE TABLE exam (
excode CHAR(4) NOT NULL PRIMARY KEY,
extitle VARCHAR(20) NOT NULL,
exlocation VARCHAR(20) NOT NULL, --I'm assuming that an exam will have a location confirmed prior to insertion into the table--
exdate DATE NOT NULL
CONSTRAINT incorrectDate
CHECK (exdate >='01/06/2015' AND exdate <= '30/06/2015'), /* I'm assuming that all exams must have a date confirmed or otherwise the exam wouldn't be inserted into the table*/
extime TIME NOT NULL, -- I'm assuming that an exam will have a time confirmed prior to insertion into the table--
CONSTRAINT incorrect_time
CHECK (extime BETWEEN '09:00:00' AND '18:00:00')
);
You don't need to use triggers for this, you can use normal table constraints, although you will need to define a function.
Your first requirement - that the same student cannot enter the same exam twice - can be checked using a UNIQUE constraint on (excode,sno). In theory this check is redundant because the second check (that a student cannot enter more than one exam per day) would also be violated by that. However, to cater for the possibility of subsequent record updates, this UNIQUE constraint is still needed.
The second requirement can be met using a CHECK constraint. However you have to create a function, because it is not possible to use a subquery inside a CHECK constraint.
Here is an example:
-- Assume we have an exams table. This table specifies the time of each exam
CREATE TABLE exams(excode SERIAL PRIMARY KEY, extime timestamp);
-- Create the entry table. We cannot add the CHECK constraint right away
-- because we have to define the function first, and the table must exist
-- before we can do that.
CREATE TABLE entry(excode int, sno int, egrade FLOAT, UNIQUE(excode,sno));
-- Create a function, which performs a query to return TRUE if there is
-- another exam already existing which this student is enrolled in that
-- is on the same day as the exame identified with p_excode
CREATE FUNCTION exam_on_day(p_excode int, p_sno int) RETURNS bool as $$
SELECT TRUE
FROM entry
LEFT JOIN exams ON entry.excode=exams.excode
WHERE sno=p_sno AND entry.excode != p_excode
AND date_trunc('day',extime)=(
SELECT date_trunc('day', extime) FROM exams WHERE excode=p_excode
);
$$ LANGUAGE SQL;
-- Add check constraint
ALTER TABLE entry ADD CONSTRAINT exam_on_same_day
CHECK(not exam_on_day(excode, sno));
-- Populate some exames.
-- excode 1
INSERT INTO exams(extime) VALUES('2014-12-06 10:00');
-- excode 2
INSERT INTO exams(extime) VALUES('2014-12-06 15:00');
-- excode 3
INSERT INTO exams(extime) VALUES('2014-12-05 15:00');
Now we can try it out:
harmic=> INSERT INTO entry(excode,sno,egrade) VALUES(1,1,98.5);
INSERT 0 1
harmic=> INSERT INTO entry(excode,sno,egrade) VALUES(1,1,50);
ERROR: duplicate key value violates unique constraint "entry_excode_sno_key"
DETAIL: Key (excode, sno)=(1, 1) already exists.
harmic=> INSERT INTO entry(excode,sno,egrade) VALUES(2,1,99);
ERROR: new row for relation "entry" violates check constraint "exam_on_same_day"
DETAIL: Failing row contains (2, 1, 99).
harmic=> INSERT INTO entry(excode,sno,egrade) VALUES(3,1,75);
INSERT 0 1
harmic=> UPDATE entry SET egrade=98 WHERE excode=1 AND sno=1;
UPDATE 1
test=> UPDATE entry SET excode=2 WHERE excode=3 AND sno=1;
ERROR: new row for relation "entry" violates check constraint "exam_on_same_day"
DETAIL: Failing row contains (2, 1, 75).
Note that I named the constraint meaningfully, so that when you get an error you can see why (useful if you have more than one constraint).
Also note that the function used for the SELECT constraint excludes the record being updated from the check (entry.excode != p_excode) otherwise you could not update any records.
You could still do this with triggers, of course, although it would be unnecessarily complicated to set up. The IF condition would be similar to the function created above.