trigger for not inserting members doesnt work - sql

I have this table
CREATE TABLE members
(
member_id INT PRIMARY KEY NOT NULL,
first_name VARCHAR(20),
last_name VARCHAR(20),
web_page VARCHAR(200),
e_mail VARCHAR(200),
cv VARCHAR(800),
dep_id INT,
teacher_id INT
);
and I want to create a trigger that if someone wants to insert a member which has a dep_id of 1 or 2 or 3.
And the teacher_id is different than NULL (as the teacher_id column is filled with either NULL or an id of another member)
I came up with this
CREATE TRIGGER employee_insup1
ON members
FOR INSERT, UPDATE
AS
DECLARE #dep_id INT, #teacher_id INT
SELECT #dep_id = i.dep_id, #teacher_id = i.teacher_id
FROM inserted i
IF ((#dep_id = 1) AND (#teacher_id != NULL))
BEGIN
RAISERROR('Teacher_id expects NULL',16,1)
ROLLBACK TRANSACTION
END
but after all if I try to insert a member with dep_id 1 and teacher_id 7(for example) it will be registered

You don't need a trigger for this. A check constraint is sufficient:
alter table members add constraint chk_members_dep_teacher
check (dep_id not in (1, 2, 3) or teacher_id is not null);
Specifically, this ensures that when dep_id is in one of those departments, then the teacher_id is not null. You might find the logic easier to follow as:
alter table members add constraint chk_members_dep_teacher
check (not (dep_id nt in (1, 2, 3) and teacher_id is null) );

Related

Oracle Trigger Syntax Using Subquery

I'm trying to write a Trigger in Oracle Syntax which, upon entering a line into a particular table, checks that both values entered belong to some classification that is held in another table. My initial thought was to have a constraint on the table that included a subquery but Oracle doesn't seem to like that.
The select query I have written in the below works - but I'm not sure how to put it into a trigger - but essentially I need the trigger to ensure that EW1.OrgId and EW2.OrgId are the same. Any help is appreciated!
CREATE TABLE Organisation (
OrgId INTEGER PRIMARY KEY,
Name VARCHAR(40) NOT NULL
);
CREATE TABLE Person (
PersonId INTEGER PRIMARY KEY,
FirstName VARCHAR(45) NOT NULL,
LastName VARCHAR(45) NOT NULL
);
CREATE TABLE Employee (
PersonId INTEGER PRIMARY KEY REFERENCES PERSON (PersonId) ON DELETE CASCADE
);
CREATE TABLE Manager (
PersonId INTEGER PRIMARY KEY REFERENCES PERSON (PersonId) ON DELETE CASCADE
);
CREATE TABLE EnlistedWith (
OrgId INTEGER REFERENCES ORGANISATION (OrgId) ON DELETE CASCADE,
PersonId INTEGER REFERENCES PERSON (PersonId) ON DELETE CASCADE,
PRIMARY KEY(OrgId,PersonId)
);
CREATE TABLE SupervisedBy (
EmployeeId INTEGER REFERENCES Employee (PersonId) ON DELETE CASCADE,
ManagerId INTEGER REFERENCES Manager (PersonId) ON DELETE CASCADE,
CONSTRAINT PK_SupervisedBy PRIMARY KEY (EmployeeId, ManagerId)
);
CREATE TRIGGER SupervisorCompany
AFTER INSERT ON SupervisedBy
FOR EACH ROW
BEGIN
declare qty INTEGER := 0;
BEGIN
SELECT COUNT (*) into qty
FROM SupervisedBy SB
INNER JOIN EnlistedWith EW1 ON SB.ManagerId = EW1.PersonId
INNER JOIN EnlistedWith EW2 ON SB.EmployeeId = EW2.PersonId
and EW1.OrgId <> EW2.OrgId
IF qty <> 0
then Raise_Error (1234567, 'Manager and Employee are not Enlisted with same Organisation');
END IF;
END;
END;
You can refer to the columns of the owner of a Trigger using :NEW / :OLD. So, your Trigger could be re-written as
CREATE OR REPLACE TRIGGER supervisorcompany AFTER
INSERT
ON supervisedby FOR EACH ROW
DECLARE qty INTEGER := 0;
BEGIN
SELECT count (*)
INTO qty
FROM enlistedwith ew1
WHERE ew1.personid = :NEW.managerid
AND EXISTS
(
SELECT 1
FROM enlistedwith ew2
WHERE ew2.personid = :NEW.employeeid
AND ew1.orgid <> ew2.orgid ) ;
IF qty <> 0 THEN
raise_application_error (1234567, 'Manager and Employee are not Enlisted with same Organisation');
END IF;
END;
/
There's some guessing involved... Maybe something like this
CREATE TRIGGER SupervisorCompany
AFTER INSERT
ON SupervisedBy
FOR EACH ROW
BEGIN
IF (SELECT OrgId
FROM EnlistedWith
WHERE PersonId = New.ManagerId)
<>
(SELECT OrgId
FROM EnlistedWith
WHERE PersonId = New.EmployeeId) THEN
RAISE_ERROR(1234567, 'Manager and Employee are not Enlisted with same Organisation');
END IF;
END;
is what you want? It checks if the OrgId of the row where the PersonId equals the newly entered ManagerId is the same as the one for the newly entered EmployeeId. If not, your error is raised.
(Untested, as no DDL for the tables were provided.)

How can this postgresql query return as table?

I am trying to call modules which in specific course but it returns as error: more than one row returned by a subquery used as an expression
Query:
CREATE OR REPLACE FUNCTION course_modules(_courseID integer)
RETURNS SETOF modules AS
$$
BEGIN
RETURN QUERY SELECT * FROM modules WHERE mod_id =(SELECT module_id from coursemodules WHERE course_id = _courseID);
END
$$
LANGUAGE 'plpgsql';
coursemodule table
CREATE TABLE coursemodules(
course_id integer references courses (id) ON DELETE CASCADE,
module_id integer references modules (mod_id) ON DELETE CASCADE
);
Modules Table
CREATE TABLE modules(
documents text NOT NULL,
mod_id serial primary key,
content text NOT NULL,
title varchar(50) NOT NULL
);
Course Table
CREATE TABLE courses(
finishDate Date,
description text NOT NULL,
duration varchar(50) NOT NULL,
startDate Date,
id serial primary key,
courseName varchar(50) NOT NULL
);
There is no restriction in the coursemodule table that a course could only have one module. Because of that the SELECT module_id from coursemodules WHERE course_id = _courseID subquery could return multiple lines.
If you change mod_id = (SELECT module_id from coursemodules WHERE course_id = _courseID)
to
mod_id IN (SELECT module_id from coursemodules WHERE course_id = _courseID).
It should work. Otherwise you have to add constraints to the coursemodule table.
This is a simple SQL syntax error. You're using
WHERE mod_id =
But you may have more than one row returning from the subquery. User IN:
WHERE mod_id IN

On update triggering in oracle?

I create 2 tables employees and customers.The employees table code is here:
create table employees(
employeeNumber number not null,
lastName varchar2(30) not null,
firstName varchar2(30) not null,
email varchar2(50) not null,
officeCode varchar2(10) not null,
assignTo number default null,
jobTitle varchar2(100) not null,
primary key (employeeNumber),
foreign key (officeCode) references offices(officeCode),
foreign key (assignTo) references employees(employeeNumber)
);
here assignTo is a foreign key of his own table employees and employeeNumber is auto increment.Some insertion sample is here:
insert into employees (lastName,firstName,email,officeCode,jobTitle)
values ('hasan','rumy','md.rejaulhasanrumy#gmail.com','123','manager');
insert into employees (lastName,firstName,email,officeCode,assignTo,jobTitle)
values ('hasan','rakib','kalorakib#gmail.com','123', 1 ,'assistant manager');
The customer table code is here:
create table customers (
customerNumber number not null,
customerName varchar2(50) not null,
phone varchar2(20) not null,
address varchar2(70) not null,
city varchar2(50) not null,
postalCode varchar2(15) not null,
country varchar2(40) not null,
salesRepEmployeeNumber number default null,
primary key(customerNumber),
foreign key (salesRepEmployeeNumber) references employees (employeeNumber)
);
customerNumber is auto increment.some sample insertion is here:
insert into customers
(customerName,phone,address,city,postalCode,country,salesRepEmployeeNumber)
values ('roxy','017456','holy park','kolia','Z143','something',1);
Now I create a trigger which execute before update employeeNumber column of employees table for on update cascade and the code is here:
create or replace trigger employees_update
before update of employeeNumber on employees
for each row
begin
update employees
set
assignTo = :new.employeeNumber
where assignTo = :old.employeeNumber;
update customers set
salesRepEmployeeNumber = :new.employeeNumber
where salesRepEmployeeNumber = :old.employeeNumber;
end;
/
above all is right in oracle but the problem is when I update employees table.The update code is here:
update employees set employeeNumber = 134 where employeeNumber = 1;
the problem is here:
ORA-04091: table RUMY.EMPLOYEES is mutating, trigger/function may not see it
ORA-06512: at "RUMY.EMPLOYEES_UPDATE", line 2
ORA-04088: error during execution of trigger 'RUMY.EMPLOYEES_UPDATE'
1. update employees set employeeNumber = 134 where employeeNumber = 1;
As far I know it's a system problem so where I make mistake?Can not I make foreign key assignTo of employees table?Also notice that same thing work properly in mysql.Advance thanks for answering this long question.
As you have discovered, you cannot select from the same table that a row-level trigger is defined against; it causes a table mutating exception.
Then - assuming at least Oracle 11, this will need to be split into individual triggers in earlier versions
CREATE OR REPLACE TRIGGER employees_update
FOR UPDATE ON employees
COMPOUND TRIGGER
TYPE employeeNumberRec IS RECORD
(oldEmployeeNumber employees.employeeNumber%TYPE
,newEmployeeNumber employees.employeeNumber%TYPE);
TYPE employeeNumbersTbl IS TABLE OF employeeNumberRec;
g_employeeNumbers employeeNumbersTbl;
BEFORE STATEMENT
IS
BEGIN
-- Reset the internal employees table
g_employeeNumbers := employeeNumbersTbl();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
-- Store the updated employees
IF :new.employeeNumber <> :old.employeeNumber THEN
g_employeeNumbers.EXTEND;
g_employeeNumbers(g_employeeNumbers.LAST).oldEmployeeNumber := :old.employeeNumber;
g_employeeNumbers(g_employeeNumbers.LAST).newEmployeeNumber := :new.employeeNumber;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
-- Now update the child tables
FORALL l_index IN 1..g_employeeNumbers.COUNT
UPDATE employees
SET assignTo = g_employeeNumbers(l_index).newEmployeeNumber
WHERE assignTo = g_employeeNumbers(l_index).oldEmployeeNumber;
FORALL l_index IN 1..g_employeeNumbers.COUNT
UPDATE customers
SET salesRepEmployeeNumber = g_employeeNumbers(l_index).newEmployeeNumber
WHERE salesRepEmployeeNumber = g_employeeNumbers(l_index).oldEmployeeNumber;
END AFTER STATEMENT;
END;
EDIT
In addition you will need to make the foreign key constraints that reference this table deferred e.g.
CREATE TABLE employees
(employeeNumber NUMBER NOT NULL
,lastName VARCHAR2(30) NOT NULL
,firstName VARCHAR2(30) NOT NULL
,email VARCHAR2(50) NOT NULL
,officeCode VARCHAR2(10) NOT NULL
,assignTo NUMBER DEFAULT NULL
,jobTitle VARCHAR2(100) NOT NULL
,PRIMARY KEY (employeeNumber)
,FOREIGN KEY (officeCode)
REFERENCES offices (officeCode)
,FOREIGN KEY (assignTo)
REFERENCES employees (employeeNumber)
DEFERRABLE
INITIALLY DEFERRED
)
and
CREATE TABLE customers
(customerNumber NUMBER NOT NULL
,customerName VARCHAR2(50) NOT NULL
,phone VARCHAR2(20) NOT NULL
,address VARCHAR2(70) NOT NULL
,city VARCHAR2(50) NOT NULL
,postalCode VARCHAR2(15) NOT NULL
,country VARCHAR2(40) NOT NULL
,salesRepEmployeeNumber NUMBER DEFAULT NULL
,PRIMARY KEY (customerNumber)
,FOREIGN KEY (salesRepEmployeeNumber)
REFERENCES employees (employeeNumber)
DEFERRABLE
INITIALLY DEFERRED
)
Note: if the constraint is violated this will cause an error at COMMIT not after an individual DML statement.
You can re-write the above trigger in following ways to avoid the issues:
create or replace trigger employees_update
before update of employeeNumber on employees
for each row
begin
emp_upd_trg_proc(:new.employeeNumber,:old.employeeNumber);
update customers set
salesRepEmployeeNumber = :new.employeeNumber
where salesRepEmployeeNumber = :old.employeeNumber;
end;
/
create or replace procedure emp_upd_trg_proc
(new number, old number)
is
pragma autonomous_transaction;
begin
update employees
set
assignTo = new
where assignTo = old;
commit;
end;
/

sqlite trigger checking equality of new tuple values with subqueries

I have a database representing a school with tables such as courses, students, and enrollment. They have the following schemas:
CREATE TABLE Students(
id INT CHECK(id > 100000),
lName CHAR(20),
fName CHAR(10),
gender CHAR(1) CHECK(gender IN('F','M')),
dob DATE,
address CHAR(100),
phone INT CHECK(phone > 1000000000),
grade CHAR(1) CHECK(grade IN('K','1','2','3','4','5')),
PRIMARY KEY(id));
CREATE TABLE Courses(
id INT CHECK(id > 1000),
name CHAR(40),
grade CHAR(1) CHECK(grade IN('K','1','2','3','4','5')),
PRIMARY KEY(id)
);
CREATE TABLE Enrollment(
studentID INT REFERENCES Students(id),
courseID INT REFERENCES Courses(id),
semester CHAR(6) CHECK(semester IN('Fall','Spring')),
year INT,
letterGrade CHAR(1) CHECK(letterGrade IN('A','B','C','D','E')),
PRIMARY KEY(studentID,courseID,semester,year));
I want to create a trigger for the enrollment table that will check that the grade level of the student with the given studentID and the grade level of the course with the courseID are equal before the tuple is inserted. I've been working on this for a while and can't seem to get it to work. Could anyone point me in the right direction?
This is what I've come up with:
CREATE TRIGGER appropriateLevel
...> BEFORE INSERT ON Enrollment
...> FOR EACH ROW
...> BEGIN
...> SELECT RAISE(ABORT,'The student's grade does not match the grade level of the course.')
...> WHERE EXISTS(SELECT * FROM Students, Courses WHERE ((Students.grade = Courses.grade) AND (Students.id = NEW.studentID) AND (Courses.id = NEW.courseID)));
...> END;
Thanks!
Your trigger aborts when the grades match.
Anyway, you don't need to use EXISTS because you want to compare only one specific value from each parent table:
CREATE TRIGGER appropriateLevel
BEFORE INSERT ON Enrollment
FOR EACH ROW
BEGIN
SELECT RAISE(ABORT, 'The student''s grade does not match the course''s grade level.')
WHERE (SELECT grade FROM Students WHERE id = NEW.studentID) <>
(SELECT grade FROM Courses WHERE id = NEW.courseID);
END;

Constrain number of rows in table, by value in another table

I have a table called ward (dealing with hospitals):
Create table ward(
Wno varchar(15) Primary Key,
Name varchar(20) Not Null,
Number_of_beds integer Not Null
);
And a table for patients:
Create table patient(
Pid varchar(15) Primary Key,
Name varchar(20) Not Null,
Address varchar(50) Not Null,
Date_of_birth date Not Null
);
I need to constrain the patients somehow so that if a patient is assigned to a specific ward then the number of patients can't exceed the number of beds in the ward.
I thought of adding the Wno as a foreign key to the patient table, but don't really know where to go from there.
You may add the foreign key into patient table as following. Apology this query is in MYSQL. I noticed that you need Oracle though. However the logic is similar :) The syntax needs changes.
Create table ward(
Wno varchar(15) Not null Primary Key,
Name varchar(20) Not Null,
Number_of_beds integer Not Null
);
And a table for patients:
Create table patient(
Pid varchar(15) Primary Key,
Name varchar(20) Not Null,
Address varchar(50) Not Null,
Date_of_birth date Not Null,
WardNo varchar(15),
foreign key (wardno) references ward (wno) ' -- adds the foreign key relation
);
In order to check if ward is full, you can have an insert or update trigger
Free bed count can be obtained by following query:
SELECT p.wardno, (w.number_of_beds - count(pid)) as freebeds
from patient as p
left join ward as w
on p.wardno = w.wno
group by wardno
Now we create a trigger to check if any patient is going into a ward whre freebed count = 0.
Updated to Oracle Version
CREATE OR REPLACE TRIGGER FreeBedsWardTrigger
BEFORE UPDATE OR INSERT
ON patient
FOR EACH ROW
DECLARE
max_beds INTEGER; -- max number of beds for the ward
used_beds INTEGER; -- used beds for the ward
BEGIN
SELECT COUNT (pid)
INTO used_beds
FROM patient
WHERE wardno = :NEW.wardno;
SELECT number_of_beds
INTO max_beds
FROM ward
WHERE wno = :NEW.wardno;
IF (max_beds - used_beds) > 0
THEN
RETURN;
ELSE
RAISE_APPLICATION_ERROR (-100100,
'No more beds available in this ward.');
END IF;
END;