feat: update structure

This commit is contained in:
2024-01-22 14:27:40 +08:00
parent 7836c9185c
commit 3544a28a2e
559 changed files with 120846 additions and 4102 deletions

View File

@@ -0,0 +1,160 @@
CREATE TABLE Offices
(
oid INTEGER,
address VARCHAR(60),
PRIMARY KEY (oid)
);
-- eid = eid of department 's manager
CREATE TABLE IF NOT EXISTS Departments
(
did INTEGER,
dbudget INTEGER NOT NULL,
oid INTEGER NOT NULL,
eid INTEGER NOT NULL, -- no FK to manager
PRIMARY KEY (did),
FOREIGN KEY (oid) REFERENCES Offices
);
CREATE TABLE IF NOT EXISTS Employees
(
eid INTEGER,
did INTEGER NOT NULL,
PRIMARY KEY (eid),
FOREIGN KEY (did) REFERENCES Departments
);
CREATE TABLE IF NOT EXISTS Engineers
(
eid INTEGER,
PRIMARY KEY (eid),
FOREIGN KEY (eid) REFERENCES Employees
);
CREATE TABLE IF NOT EXISTS Managers
(
eid INTEGER,
PRIMARY KEY (eid),
FOREIGN KEY (eid) REFERENCES Employees
);
-- eid = eid of project's supervisor
CREATE TABLE IF NOT EXISTS Projects
(
pid INTEGER,
pbudget INTEGER NOT NULL,
eid INTEGER NOT NULL,
PRIMARY KEY (pid),
FOREIGN KEY (eid) REFERENCES Managers
);
CREATE TABLE IF NOT EXISTS WorkType
(
wid INTEGER,
max_hours INTEGER NOT NULL,
PRIMARY KEY (wid)
);
CREATE TABLE IF NOT EXISTS Works
(
pid INTEGER,
eid INTEGER,
wid INTEGER,
hours INTEGER NOT NULL,
PRIMARY KEY (pid, eid),
FOREIGN KEY (eid) REFERENCES Engineers,
FOREIGN KEY (pid) REFERENCES Projects,
FOREIGN KEY (wid) REFERENCES WorkType
ON DELETE CASCADE
);
CREATE OR REPLACE FUNCTION not_manager()
RETURNS TRIGGER AS
$$
DECLARE
count NUMERIC;
BEGIN
SELECT COUNT(*) INTO count FROM managers WHERE NEW.eid = Managers.eid;
if count > 0 THEN RETURN NULL; ELSE RETURN NEW; END IF;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER non_manager
BEFORE INSERT OR UPDATE
ON Engineers
FOR EACH ROW
EXECUTE FUNCTION not_manager();
CREATE OR REPLACE FUNCTION check_budget()
RETURNS TRIGGER AS
$$
DECLARE
pbudget numeric;
curr_budget numeric;
BEGIN
SELECT pbudget
INTO pbudget
FROM projects
WHERE pid = NEW.pid;
SELECT COALESCE(SUM(hours), 0) * 100
INTO curr_budget
FROM Works
WHERE pid = NEW.pid
AND eid != NEW.eid;
if curr_budget + NEW.hours * 100 > pbudget THEN
RETURN (NEW.pid, NEW.eid, NEW.wid, (pbudget - curr_budget) / 100);
ELSE
RETURN NEW;
end if;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER budget_check
BEFORE INSERT OR UPDATE
ON Works
FOR EACH ROW
EXECUTE FUNCTION check_budget();
CREATE OR REPLACE FUNCTION max_hour_work()
RETURNS TRIGGER AS
$$
DECLARE
max_hrs NUMERIC;
BEGIN
SELECT max_hours INTO max_hrs FROM worktype WHERE wid = NEW.wid;
IF NEW.HOURS > max_hrs THEN
RETURN (NEW.pid, NEW.eid, NEW.wid, max_hrs);
ELSE
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER hours_max
BEFORE INSERT OR UPDATE
ON works
FOR EACH ROW
EXECUTE FUNCTION max_hour_work();
CREATE OR REPLACE FUNCTION wid_check()
RETURNS TRIGGER AS
$$
DECLARE
BEGIN
RAISE NOTICE 'some user tried to';
RAISE NOTICE 'modify/delete default';
RAISE NOTICE 'work type';
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER check_wid
BEFORE UPDATE OR DELETE
ON WorkType
FOR EACH ROW
WHEN (OLD.wid = 0)
EXECUTE FUNCTION wid_check();

View File

@@ -0,0 +1,71 @@
INSERT INTO book VALUES (
'An Introduction to Database Systems',
'paperback' ,
640 ,
'English' ,
'C. J. Date' ,
'Pearson',
'0321197844' ,
'978-0321197849');
INSERT INTO student VALUES (
'TIKKI TAVI' ,
'tikki@gmail.com' ,
'2021-08-01',
'School of Computing',
'CS',
NULL);
INSERT INTO student (email, name, year, faculty, department)
VALUES (
'rikki@gmail.com',
'RIKKI TAVI',
'2021-08-01',
'School of Computing',
'CS');
UPDATE student
SET department = 'Computer Science'
WHERE department = 'CS';
-- Does not work as students references loans
-- DELETE FROM student WHERE department = 'Chemistry';
-- remove foreign key constraint
ALTER TABLE loan DROP CONSTRAINT loan_borrower_fkey;
-- add new foreign key constraint
ALTER TABLE loan ADD CONSTRAINT loan_borrower_fkey
FOREIGN KEY (borrower) REFERENCES student(email) ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE ;
DELETE FROM student where department = 'Chemistry';
INSERT INTO copy
VALUES (
'tikki@gmail.com',
'978-0321197849',
1);
BEGIN TRANSACTION;
SET CONSTRAINTS ALL IMMEDIATE;
DELETE FROM book
WHERE ISBN13 = '978-0321197849' ;
DELETE FROM copy
WHERE book = '978-0321197849' ;
END TRANSACTION;
BEGIN TRANSACTION;
SET CONSTRAINTS ALL DEFERRED;
DELETE FROM book WHERE ISBN13 = '978-0321197849' ;
DELETE FROM copy WHERE book = '978-0321197849' ;
END TRANSACTION;
ALTER TABLE copy ADD COLUMN available BOOLEAN DEFAULT true;
UPDATE copy
SET available = false
WHERE (owner, book, copy) IN (SELECT owner, book, copy FROM loan WHERE returned ISNULL);
-- Disagree, the departments and fields are not the same.

View File

@@ -0,0 +1,90 @@
-- 1a
SELECT distinct s.department
FROM student s;
-- Print the different departments in which students are enrolled
select distinct department
from student;
-- Email of students who borrowed or lent copy before uni
SELECT DISTINCT s.email
FROM student s, loan l
WHERE (s.email = l.borrower OR s.email = l.owner) AND s.year > l.borrowed;
-- For each copy borrowed / returned, print duration. Order in ASC ISBN13 and desc duration
SELECT l.returned - l.borrowed as duration, l.book
FROM loan l
WHERE returned IS NOT NULL
order by l.book ASC, duration DESC;
-- For each book published by wiley that has not been returned ,print title, name, faculty, (name, faculty of borrower)
select b.title,
owner.name,
owner.faculty,
borrower.name,
borrower.faculty
from loan l,
book b,
student owner, student borrower
WHERE b.isbn13 = l.book
AND owner.email = l.owner
AND borrower.email = l.borrower
AND b.publisher = 'Wiley'
AND returned is null;
-- For each book published by wiley that has not been returned ,print title, name, faculty, (name, faculty of borrower)select b.title, owner.name, owner.faculty, borrower.name, borrower.faculty
SELECT b.title,
owner.name,
owner.faculty,
borrower.name,
borrower.faculty
FROM loan l
INNER JOIN book b on b.isbn13 = l.book
INNER JOIN student owner on owner.email = l.owner
INNER JOIN student borrower on borrower.email = l.borrower
WHERE b.publisher = 'Wiley'
AND returned is null;
-- Print email of different studnt who borrowed or lent a book on the day they joined uni
SELECT s.email
FROM loan l, student s
WHERE s.email = l.borrower AND l.borrowed = s.year
UNION
SELECT s.email
FROM loan l, student s
WHERE s.email = l.owner AND l.borrowed = s.year;
-- OR
SELECT DISTINCT s.email
FROM loan l, student s
WHERE (s.email = l.borrower OR s.email = l.owner)
AND l.borrowed = s.year;
-- Print email of different studnt who borrowed or lent a book on the day they joined uni
SELECT s.email
FROM loan l, student s
WHERE s.email = l.borrower AND l.borrowed = s.year
INTERSECT
SELECT s.email
FROM loan l, student s
WHERE s.email = l.owner AND l.borrowed = s.year;
-- Print the emails of the students who borrowed but did not lend a copy of a book on the day that they joined the university
SELECT s.email
FROM loan l, student s
WHERE s.email = l.borrower AND l.borrowed = s.year
EXCEPT
SELECT s.email
FROM loan l, student s
WHERE s.email = l.owner AND l.borrowed = s.year;
-- Print the ISBN13 of the books (not the copies) that have never been borrowed
SELECT b.ISBN13
FROM book b
EXCEPT
SELECT l.book
FROM loan l;
SELECT b.ISBN13
FROM book b LEFT OUTER JOIN loan l ON b.isbn13 = l.book
WHERE l.book IS NULL;

View File

@@ -0,0 +1,65 @@
-- How many loans involve an owner and a borrower from the same department?
SELECT COUNT(*)
FROM loan l,
student owner,
student borrower
WHERE l.owner = owner.email
AND l.borrower = borrower.email
AND owner.department = borrower.department;
-- For each faculty, print the number of loans that involve an owner and a borrower from this faculty?
SELECT owner.department, COUNT(*)
FROM loan l,
student owner,
student borrower
WHERE l.owner = owner.email
AND l.borrower = borrower.email
AND owner.department = borrower.department
GROUP BY owner.department;
-- What are the average and the standard deviation of the duration of a loan? Round the results to the nearest integer.
SELECT ROUND(AVG(l.returned - l.borrowed+1),0), ROUND(stddev_pop(l.returned - l.borrowed+1),0)
FROM loan l;
-- (a) Print the titles of the different books that have never been borrowed. Use a nested query.
SELECT b.title
FROM book b
WHERE b.isbn13 NOT IN (
SELECT l.book
FROM loan l
);
-- (b) Print the name of the different students who own a copy of a book that they have never lent to anybody.
SELECT s.name
FROM student s
WHERE s.email IN (
SELECT c.owner
FROM copy c
WHERE (c.owner, c.book,c.copy)NOT IN (
SELECT l.owner,l.book,l.copy
FROM loan l
) );
-- For each department, print the names of the students who lent the most.
SELECT s.name,s.department, COUNT(*)
FROM student s,loan l
WHERE l.owner = s.email
GROUP BY s.name, s.department
HAVING COUNT(*) >= ALL
(SELECT COUNT(*) FROM student s1, loan l1 WHERE l1.owner = s1.email AND s.department = s1.department GROUP BY s1.email);
-- Print email and name of different students who borrowed all books authored by adam smith
SELECT s.email, s.name
FROM student s
WHERE NOT EXISTS(
SELECT *
FROM book b
WHERE b.authors = 'Adam Smith'
AND NOT EXISTS(
SELECT *
FROM loan l
WHERE l.book = b.isbn13
AND l.borrower = s.email
)
)

View File

@@ -0,0 +1,43 @@
-- How many loans involve
SELECT b.title
FROM book b
WHERE b.isbn13 != ALL (
SELECT l.book FROM loan l
);
-- 2b
SELECT s.name
FROM student s
WHERE s.email = ANY (
SELECT c.owner
FROM copy c
WHERE (c.owner, c.book, c.copy) NOT IN (
SELECT l.owner, l.book, l.copy
FROM loan l
)
);
-- 2c (When you see the most/the least, there's a standard way
-- of solving this
SELECT s.department, s.name, COUNT(*)
FROM student s, loan l
WHERE l.owner = s.email
GROUP BY s.department, s.name, s.email
HAVING COUNT(*) >= ALL (
SELECT COUNT(*)
FROM student s1, loan l1
WHERE l1.owner = s1.email
AND s.department = s1.department
GROUP BY s1.email
)
ORDER BY COUNT(*) DESC;
-- 2d
SELECT s.name, s.email
FROM student s
WHERE s.email = ALL (
SELECT c
FROM book b
WHERE b.authors LIKE '%Adam Smith%'
)

View File

@@ -0,0 +1,79 @@
CREATE OR REPLACE FUNCTION max_min(IN stu_id INT, OUT max_cid INT, OUT min_cid INT)
RETURNS RECORD AS
$$
DECLARE
max_score INT;
min_score INT;
BEGIN
SELECT e.score, e.cid
INTO max_score, max_cid
FROM exams e
WHERE stu_id = e.sid
AND e.score = (SELECT MAX(score)
FROM exams
where stu_id = sid);
SELECT e.score, e.cid
INTO min_score, min_cid
FROM exams e
WHERE stu_id = e.sid
AND e.score = (SELECT MIN(score)
FROM exams
where stu_id = sid);
IF max_score = min_score THEN
min_cid = NULL;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION revised_avg(IN stu_id INT, OUT r_avg FLOAT)
RETURNS FLOAT AS
$$
DECLARE
max_score INT;
min_score INT;
sum_score FLOAT;
count_score INT;
BEGIN
SELECT MAX(score), MIN(score), SUM(score), COUNT(score)
INTO max_score, min_score, sum_score, count_score
FROM exams
WHERE sid = stu_id;
if count_score < 3 THEN
r_avg := NULL;
ELSE
r_avg := (sum_score - max_score - min_score) / (count_score - 2);
end if;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION list_r_avg()
RETURNS TABLE
(
stu_id INT,
ravg FLOAT
)
AS
$$
DECLARE
curs CURSOR FOR (SELECT sid, score
FROM Exams
ORDER BY sid);
r RECORD;
/* add other variables here */
BEGIN
/* write your code here */
stu_id := -1;
OPEN curs;
LOOP
FETCH curs INTO r;
if r.sid <> "".stu_idOR NOT FOUND THEN
end if;
end loop;
CLOSE curs;
END;
$$ LANGUAGE plpgsql;

View File

@@ -0,0 +1,151 @@
DROP TABLE IF EXISTS exams;
CREATE TABLE IF NOT EXISTS
exams
(
SID INT,
CID INT,
score INT,
PRIMARY KEY (SID, CID)
);
CREATE OR REPLACE FUNCTION max_min(IN stu_id INT, OUT max_cid INT, OUT min_cid INT)
RETURNS RECORD AS
$$
DECLARE
max_score INT; min_score INT;
BEGIN
SELECT e.score, e.cid
INTO max_cid, max_score
FROM exams e
WHERE e.sid = stu_id
AND e.score = (SELECT MAX(score) from exams);
SELECT e.score, e.cid
INTO min_cid, min_score
FROM exams e
WHERE e.sid = stu_id
AND e.score = (SELECT MIN(score) from exams);
IF min_score = max_score THEN
min_cid := NULL;
end if;
/* write your code here */
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION revised_avg(IN stu_id INT, OUT r_avg FLOAT)
RETURNS FLOAT AS
$$
DECLARE
max_score INT;
min_score INT;
count_score INT;
sum_score INT;
BEGIN
SELECT MAX(score), MIN(score), COUNT(score), SUM(score)
INTO max_score, min_score, count_score, sum_score
FROM exams;
IF count_score < 3 THEN
r_avg := NULL;
ELSE
r_avg := (sum_score - min_score - max_score) / (count_score - 2);
END IF;
END;
/* write your code here */
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION list_r_avg()
RETURNS TABLE
(
stu_id INT,
ravg FLOAT
)
AS
$$
DECLARE
curs CURSOR FOR (SELECT sid, score
FROM Exams
ORDER BY sid);
curr RECORD;
sum_score INT;
count_score INT;
max_score INT;
min_score INT;
/* add other variables here */
BEGIN
-- to validate first row
stu_id := -1;
OPEN curs;
LOOP
FETCH curs INTO curr;
IF curr.sid != stu_id OR NOT FOUND THEN
IF stu_id != -1 THEN
IF (count_score < 3) THEN
ravg := NULL;
ELSE
ravg := (sum_score - max_score - min_score) / (count_score - 2);
END IF;
RETURN NEXT;
END IF;
EXIT WHEN NOT FOUND;
stu_id := curr.sid;
max_score := curr.score;
min_score := curr.score;
sum_score := curr.score;
count_score := 1;
ELSE
sum_score := curr.score + sum_score;
count_score := count_score + 1;
IF max_score < curr.score THEN max_score := curr.score; END IF;
IF min_score > curr.score THEN min_score := curr.score; END IF;
END IF;
END LOOP;
CLOSE curs;
/* write your code here */
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION list_scnd_highest()
RETURNS TABLE
(
stu_id INT,
scnd_highest INT
)
AS
$$
DECLARE
curs CURSOR FOR (SELECT sid, score
FROM Exams
ORDER BY sid);
curr RECORD;
max_score INT;
count_score INT;
BEGIN
stu_id := -1;
OPEN curs;
LOOP
FETCH curs INTO curr;
IF curr.sid != stu_id OR NOT FOUND THEN
IF stu_id <> -1 THEN
IF (count_score < 2) THEN scnd_highest := NULL; END IF;
RETURN NEXT;
end if;
EXIT WHEN NOT FOUND;
stu_id := curr.sid;
max_score := curr.score;
scnd_highest := -1;
count_score := 1;
ELSE
IF curr.score > max_score THEN
scnd_highest := max_score;
max_score := curr.score;
ELSEIF curr.score > scnd_highest THEN
scnd_highest := curr.score;
END IF;
count_score := count_score + 1;
END IF;
END LOOP;
CLOSE curs;
RETURN;
END;
$$ LANGUAGE plpgsql;