sqlite trigger checking equality of new tuple values with subqueries - sql

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;

Related

trigger for not inserting members doesnt work

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

PostgreSQL table join

I have a lecture attendance database as an uni project.
As one of views, i came up with idea that I could make missed_lectures view with all records of Not attended or Sick attendance types. When I am making this query, it returns 2,5k rows, which is not correct.
Query looks like this
CREATE OR REPLACE VIEW missed_lectures AS
SELECT CONCAT(s.student_name, ' ', s.student_surname) AS "Student", sc.course_title AS "Study course", atp.attendance_type AS "Attendance type", a.record_date AS "Date"
FROM students AS s, study_courses AS sc, attendance_type AS atp, attendance AS a
WHERE
s.student_id=a.student_id AND
sc.course_id=a.course_id AND
a.attendance_type_id=atp.attendance_type_id AND
a.attendance_type_id=(SELECT attendance_type_id FROM attendance_type WHERE attendacne_type='Sick') OR
a.attendance_type_id=(SELECT attendance_type_id FROM attendance_type WHERE attendance_type='Not attended')
GROUP BY s.student_name, s.student_surname, sc.course_title, atp.attendance_type, a.record_date;
This is the last query I came up with, but as I mentioned earlier, it returns 2,5k rows with incorrect data.
Can anybody spot the issue here?
EDIT:
table students is
CREATE TABLE students(
student_id serial PRIMARY KEY NOT NULL,
student_name VARCHAR(30) NOT NULL,
student_surname VARCHAR(35) NOT NULL,
matriculation_number VARCHAR(7) NOT NULL CHECK(matriculation_number ~ '[A-Z]{2}[0-9]{5}'),
faculty_id INT NOT NULL,
course INT NOT NULL,
phone_number CHAR(8) CHECK(phone_number ~ '^2{1}[0-9]{7}'),
email VARCHAR(35),
gender VARCHAR(10)
);
sample data:
INSERT INTO students (student_name, student_surname, matriculation_number, faculty_id, course, phone_number, email, gender)
VALUES
('Sandis','Bērziņš','IT19047',7,1,'25404213','sandis.berzins#gmail.com','man'),
('Einārs','Kļaviņš','IT19045',7,1,'24354654','einars.klavins#gmail.com','man'),
('Jana','Lapa','EF18034',8,2,'26224941','lapajana#inbox.lv','woman'),
('Sanija','Bērza','EF18034',8,2,'24543433','berzasanija#inbox.lv','woman'),
('Valdis','Sijāts','TF19034',4,1,'25456545','valdis.sijats#gmail.com','man'),
('Jānis','Bānis','IT17034',7,3,'24658595','banis.janis#inbox.lv','man');
table study_courses is
CREATE TABLE study_courses(
course_id serial PRIMARY KEY NOT NULL,
course_title VARCHAR(55) NOT NULL,
course_code VARCHAR(8) NOT NULL CHECK(course_code ~ '[a-zA-Z]{4}[0-9]{4}'),
credit_points INT
);
sample data:
INSERT INTO study_courses (course_title, course_code, credit_points)
VALUES
('Fundamentals of Law','JurZ2005',2),
('Database technologies II','DatZ2005',2),
('Product processing','PārZ3049',4),
('Arhitecture','Arhi3063',3),
('Forest soils','LauZ1015',4);
Table attendance_type is:
CREATE TABLE attendance_type(
attendance_type_id serial PRIMARY KEY NOT NULL,
attendance_type VARCHAR(15) NOT NULL
);
sample data:
INSERT INTO attendance_type (attendance_type)
VALUES
('Attended'),
('Not attended'),
('Late'),
('Sick');
table attendance is:
CREATE TABLE attendance(
record_id serial PRIMARY KEY NOT NULL,
student_id INT NOT NULL,
course_id INT NOT NULL,
attendance_type_id INT NOT NULL,
lecturer_id INT,
lecture_type_id INT NOT NULL,
audience_id INT NOT NULL,
record_date DATE NOT NULL
);
sample data:
INSERT INTO attendance (student_id, course_id, attendance_type_id, lecturer_id, lecture_type_id, audience_id, record_date)
VALUES
(1,2,1,1,1,14,'20-05-2020'),
(2,2,1,1,1,14,'20-05-2020'),
(6,9,1,13,2,2,'20-05-2020'),
(22,9,2,13,2,2,'20-05-2020'),
(24,9,3,13,2,2,'20-05-2020');
Hoping this will help.
The problem is that OR condition in your WHERE clause.
You have to surround it by parentheses if you're going to do it that way.
However, you're already querying the attendance_type table so you can just use it to filter for those two attendance type conditions.
SELECT
CONCAT(s.student_name, ' ', s.student_surname) AS "Student",
sc.course_title AS "Study course",
atp.attendance_type AS "Attendance type",
a.record_date AS "Date"
FROM
students AS s, study_courses AS sc,
attendance_type AS atp, attendance AS a
WHERE
s.student_id=a.student_id AND
sc.course_id=a.course_id AND
a.attendance_type_id=atp.attendance_type_id AND
-- filter for these two conditions without subqueries
atp.attendance_type in ('Sick','Not attended')
GROUP BY
CONCAT(s.student_name, ' ', s.student_surname), sc.course_title,
atp.attendance_type, a.record_date;

SQL - Subquery, SELECT from select

My database query :
CREATE DATABASE [College Assignment]
PRINT 'CREATE DATABASE College Assignment'
GO
USE [College Assignment]
CREATE TABLE Departments
(DEPART nvarchar(255) NOT NULL, D_NAME nvarchar(255), HEAD nvarchar(255),PRIMARY KEY (DEPART));
CREATE TABLE Courses
(COURSE_ID nvarchar(255) NOT NULL,
COURSE_NAME nvarchar(255),
"TYPE" nvarchar(255),
POINTS float,
DEPARTMENT_ID nvarchar(255),
PRIMARY KEY (COURSE_ID),
CONSTRAINT fk_dep
FOREIGN KEY (DEPARTMENT_ID) REFERENCES Departments(DEPART));
CREATE TABLE Students
(STUDENT_ID float NOT NULL,
S_NAME nvarchar(255),
CITY nvarchar(255),
PRIMARY KEY (STUDENT_ID));
CREATE TABLE Grades
(STUDENT_ID float NOT NULL,
COURSE_ID nvarchar(255) NOT NULL,
SEMESTER nvarchar(255),
TERM nvarchar(255),
GRADE smallint,
GRADE_SEM smallint,
PRIMARY KEY (STUDENT_ID, COURSE_ID),
CONSTRAINT fk_student
FOREIGN KEY (STUDENT_ID) REFERENCES Students(STUDENT_ID),
CONSTRAINT fk_course
FOREIGN KEY (COURSE_ID) REFERENCES Courses(COURSE_ID));
INSERT INTO Departments VALUES
('BS','Buisnes','Dr.Eyal'),
('CH','Chemistry','Prof.Doron'),
('CS','Computer Science','Dr.Israel'),
('MT','Mathematics','Prof.Levi');
INSERT INTO Courses VALUES
('B-10','Marketing','CLASS',5,'BS'),
('B-40','Operations Res.','SEMIN',3,'BS'),
('C-200','Programing','LAB',4,'CS'),
('C-300','Pascal','LAB',4,'CS'),
('C-55','Data Base','CLASS',3,'CS'),
('M-100','Linear Algebra','CLASS',3,'MT'),
('M-200','Numeric Analyses','CLASS',3,'MT');
INSERT INTO Students VALUES
(105,'Moshe','Haifa'),
(107,'Eyal','Tel Aviv'),
(110,'Ran','Haifa'),
(200,'David','Tel Aviv'),
(210,'Dan','Tel Aviv'),
(240,'Ayelet','Tel Aviv'),
(245,'Yoel','Haifa'),
(310,'Tova','Jerusalem');
INSERT INTO Grades VALUES
(105,'B-40','WIN1999','B',70,70),
(105,'C-200','AUT1999','A',90,85),
(105,'C-55','SUM1998','A',58,70),
(105,'M-100','SUM1998','B',75,50),
(200,'B-10','AUT1999','A',70,65),
(200,'C-200','AUT1999','B',78,50),
(200,'M-100','SUM1998','B',90,90),
(210,'B-10','WIN1999','A',78,50),
(210,'C-200','AUT1999','A',85,80),
(210,'M-100','AUT1999','A',90,90),
(245,'B-10','AUT1999','A',80,70),
(245,'B-40','WIN1998','A',85,95),
(245,'M-100','AUT1999','A',90,80),
(310,'M-100','SUM1998','A',65,100);
Now what I'm trying to do is to print all course names and student names where their grade multiplied by 1.1 is bigger than semester grade.
Now, I manage to do it by printing the course_id and student_id but then on this result I'm trying to connect between the student_id and their names in order to actually print the student name (s_name) and course name instead (course_id)
this is the current script I have:
SELECT STUDENT_ID,COURSE_ID
FROM Grades
WHERE (Grade*1.1>GRADE_SEM)
so basically all I want is quite the same results as I get but to have
student name (s_name) and course_name instead their id's. (this is the print I get right now
Actually it's simple Join Can do that
SELECT
Students.S_Name, Courses.Course_Name,
Students.Student_ID, Courses.Course_ID
FROM
Grades
INNER JOIN Students on Students.Student_ID = Grades.Student_ID
INNER JOIN Courses on Courses.Course_ID = Grades.Course_ID
WHERE
(Grades.Grade*1.1>Grades.GRADE_SEM)
You can read Join more in here Join Documentation
You can see here for Demo = Demo

Cross Referencing Table Value for Another Value to allow Insert

How do I look at one table and use it to check another table for data integrity?
I have two SQL Tables.
One that is a person table
CREATE TABLE PERSON
(
ID INT IDENTITY(10000,1) NOT NULL,
Firstname VARCHAR(15),
Lastname VARCHAR(25) NOT NULL,
Birthdate DATE,
Gender VARCHAR(1),
CHECK ( GENDER IN ('M', 'F')),
Street VARCHAR(50),
City VARCHAR(15),
State VARCHAR(2),
CHECK (State IN ('FL','GA','PA')),
Zip INT,
Phone VARCHAR(10),
Employee VARCHAR(1),
CHECK ( Employee IN('Y','N')),
Member VARCHAR(1),
CHECK ( Member IN('Y','N')),
CHECK (Member IN ('Y') or Employee IN ('Y')),
CONSTRAINT PERSON_PK PRIMARY KEY (ID));
And Employee Table
CREATE TABLE EMPLOYEE
(
ID INT NOT NULL,
Datehired DATE DEFAULT GETDATE(),
Status VARCHAR(1),
CHECK ( Status IN ('F','C')),
Position VARCHAR(25),
EmpType VARCHAR(25),
CONSTRAINT EMPLOYEE_PK PRIMARY KEY (ID),
CONSTRAINT EMPLOYEE_PERSON_FK FOREIGN KEY (ID) REFERENCES PERSON);
Let's say someone isn't an employee. I can still insert them into the Employee Table.
INSERT INTO EMPLOYEE
(ID, Status, Position, EmpType)
VALUES
('10000','C','Teaching Classes','Instructor');
How Do I prevent this from happening.
One method is to have a redundant key:
alter table person
contraint unq_person_id_employee
unique (id, employee);
Then add a computed column to employee:
alter table employee add employee as ('Y') persisted;
Finally, add the constraint:
alter table employee
add constraint fk_employee_person
foreign key (id, employee) references person(id, employee);
Now, you are guaranteed that only employees are in the Employee table.

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;