1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
DROP DATABASE IF EXISTS LibraryMiniWorld;
CREATE DATABASE IF NOT EXISTS LibraryMiniWorld;
USE LibraryMiniWorld;
CREATE TABLE Person (
PersonID INTEGER PRIMARY KEY AUTO_INCREMENT,
Phone VARCHAR(20) NOT NULL,
Name VARCHAR(50) NOT NULL,
Email VARCHAR(100) NOT NULL,
Address VARCHAR(255) NOT NULL
);
CREATE TABLE Customer (
PersonID INTEGER PRIMARY KEY,
JoinDate DATE NOT NULL,
FOREIGN KEY (PersonID) REFERENCES Person(PersonID)
);
CREATE TABLE LibraryCard (
CardNo INTEGER PRIMARY KEY AUTO_INCREMENT,
CustomerID INTEGER NOT NULL,
IssueDate DATE NOT NULL,
ExpiryDate DATE NOT NULL,
FOREIGN KEY (CustomerID) REFERENCES Customer(PersonID)
);
CREATE TABLE Employee (
PersonID INTEGER PRIMARY KEY,
StartDate DATE NOT NULL,
Salary INTEGER NOT NULL,
EmployeeType VARCHAR(20) NOT NULL, -- Either "Librarian" or "Manager"
FOREIGN KEY (PersonID) REFERENCES Person(PersonID)
);
CREATE TABLE Manager (
EmployeeID INTEGER PRIMARY KEY,
FOREIGN KEY (EmployeeID) REFERENCES Employee(PersonID)
);
CREATE TABLE Librarian (
EmployeeID INTEGER PRIMARY KEY,
FOREIGN KEY (EmployeeID) REFERENCES Employee(PersonID)
);
CREATE TABLE Manages (
ManagerID INTEGER NOT NULL,
LibrarianID INTEGER NOT NULL,
FOREIGN KEY (ManagerID) REFERENCES Manager(EmployeeID),
FOREIGN KEY (LibrarianID) REFERENCES Librarian(EmployeeID),
PRIMARY KEY (ManagerID, LibrarianID)
);
CREATE TABLE Media (
MediaID INTEGER PRIMARY KEY AUTO_INCREMENT,
DeweyNumber VARCHAR(20) NOT NULL,
Name VARCHAR(100) NOT NULL,
MediaType VARCHAR(20) NOT NULL, -- Either "Text", "Audio", or "Video"
CreatedYear YEAR NOT NULL
);
CREATE TABLE Genre (
GenreID INTEGER PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE Tag (
TagID INTEGER PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE Creator (
CreatorID INTEGER PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50) NOT NULL
);
CREATE TABLE MediaGenre (
MediaID INTEGER NOT NULL,
GenreID INTEGER NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID),
FOREIGN KEY (GenreID) REFERENCES Genre(GenreID),
PRIMARY KEY (MediaID, GenreID)
);
CREATE TABLE MediaTag (
MediaID INTEGER NOT NULL,
TagID INTEGER NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID),
FOREIGN KEY (TagID) REFERENCES Tag(TagID),
PRIMARY KEY (MediaID, TagID)
);
CREATE TABLE MediaCreator (
MediaID INTEGER NOT NULL,
CreatorID INTEGER NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID),
FOREIGN KEY (CreatorID) REFERENCES Creator(CreatorID),
PRIMARY KEY (MediaID, CreatorID)
);
CREATE TABLE Text (
MediaID INTEGER PRIMARY KEY,
Pages INTEGER NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID)
);
CREATE TABLE Audio (
MediaID INTEGER PRIMARY KEY,
Playtime INTEGER NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID)
);
CREATE TABLE Video (
MediaID INTEGER PRIMARY KEY,
Runtime INTEGER NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID)
);
CREATE TABLE MediaInstance (
InstanceID INTEGER PRIMARY KEY AUTO_INCREMENT,
LibrarianID INTEGER NOT NULL,
MediaID INTEGER NOT NULL,
DateAdded DATE NOT NULL,
FOREIGN KEY (MediaID) REFERENCES Media(MediaID),
FOREIGN KEY (LibrarianID) REFERENCES Librarian(EmployeeID)
);
CREATE TABLE CustomerRate (
CustomerID INTEGER NOT NULL,
MediaID INTEGER NOT NULL,
Stars INTEGER NOT NULL CHECK (Stars >= 0 AND Stars <= 5),
Review VARCHAR(690),
FOREIGN KEY (CustomerID) REFERENCES Customer(PersonID),
FOREIGN KEY (MediaID) REFERENCES Media(MediaID),
PRIMARY KEY (CustomerID, MediaID)
);
CREATE TABLE Borrow (
LibraryCardNo INTEGER NOT NULL,
InstanceID INTEGER NOT NULL,
LibrarianID INTEGER NOT NULL,
IsReturned BOOLEAN NOT NULL,
BorrowDate DATE NOT NULL,
ReturnDate DATE NOT NULL,
CONSTRAINT BorrowDateBeforeReturn CHECK (BorrowDate <= ReturnDate),
FOREIGN KEY (LibraryCardNo) REFERENCES LibraryCard(CardNo),
FOREIGN KEY (InstanceID) REFERENCES MediaInstance(InstanceID),
FOREIGN KEY (LibrarianID) REFERENCES Librarian(EmployeeID),
PRIMARY KEY (InstanceID, BorrowDate)
);
ALTER TABLE Creator ADD FULLTEXT (Name);
ALTER TABLE Tag ADD FULLTEXT (Name);
ALTER TABLE Genre ADD FULLTEXT (Name);
ALTER TABLE Media ADD FULLTEXT (Name, MediaType, DeweyNumber);
CREATE VIEW MediaQuery
AS
SELECT me.*,
GROUP_CONCAT(DISTINCT(c.name)) AS Creators,
GROUP_CONCAT(DISTINCT(t.name)) AS Tags,
GROUP_CONCAT(DISTINCT(g.name)) AS Genres,
COUNT(DISTINCT(mi.InstanceID)) AS Copies,
COUNT(DISTINCT(mi.InstanceID)) - COUNT(DISTINCT(b.InstanceID)) AS AvailableCopies
FROM Media AS me
INNER JOIN MediaInstance AS mi ON me.MediaID = mi.MediaID
LEFT JOIN MediaGenre AS mg ON me.MediaID = mg.MediaID
LEFT JOIN Genre AS g ON mg.GenreID = g.GenreID
LEFT JOIN MediaTag AS mt ON me.MediaID = mt.MediaID
LEFT JOIN Tag AS t ON mt.TagID = t.TagID
LEFT JOIN MediaCreator AS mc ON me.MediaID = mc.MediaID
LEFT JOIN Creator AS c ON mc.CreatorID = c.CreatorID
LEFT JOIN Borrow AS b ON b.InstanceID = mi.InstanceID AND b.IsReturned
GROUP BY me.MediaID;
DELIMITER //
CREATE TRIGGER BorrowCheck
BEFORE INSERT ON Borrow
FOR EACH ROW
BEGIN
IF NEW.InstanceID IN
(SELECT InstanceID FROM Borrow AS b
WHERE New.BorrowDate <= b.ReturnDate
AND New.ReturnDate >= b.BorrowDate
AND NOT b.IsReturned)
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Media instance is already borrowed';
END IF;
END;
//
DELIMITER //
CREATE TRIGGER BorrowNowOrFuture
BEFORE INSERT ON Borrow
FOR EACH ROW
BEGIN
IF NEW.BorrowDate < CURDATE() AND NEW.ReturnDate >= CURDATE() THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Borrow date cannot be in the past unless return date is as well';
END IF;
END;
//
DELIMITER //
CREATE TRIGGER CustomerCantRegisterForNewCardWhenHasOneNotExpired
BEFORE INSERT ON LibraryCard
FOR EACH ROW
BEGIN
IF NEW.ExpiryDate >= CURDATE() AND NEW.CustomerID IN
(SELECT CustomerID FROM LibraryCard WHERE ExpiryDate >= CURDATE())
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Customer already has an active library card';
END IF;
END;
//
DELIMITER //
CREATE TRIGGER LibraryCardNotExpired
BEFORE INSERT ON Borrow
FOR EACH ROW
BEGIN
IF NOT EXISTS
(SELECT CardNo FROM LibraryCard
WHERE CardNo = NEW.LibraryCardNo
AND ExpiryDate >= NEW.BorrowDate)
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Library card is expired';
END IF;
END;
//
DELIMITER //
CREATE TRIGGER CustomerCanOnlyRateMediaTheyBorrowed
BEFORE INSERT ON CustomerRate
FOR EACH ROW
BEGIN
IF New.CustomerID NOT IN
(SELECT lc.CustomerID
FROM Borrow AS b, MediaInstance AS i, Media AS m, LibraryCard AS lc
WHERE b.InstanceID = i.InstanceID
AND b.LibraryCardNo = lc.CardNo
AND i.MediaID = m.MediaID
AND m.MediaID = New.MediaID)
THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Customer can only rate media they borrowed';
END IF;
END;
//
DELIMITER //
CREATE PROCEDURE
borrowedHistory( customerID INT )
BEGIN
SELECT mi.MediaID, me.Name, b.BorrowDate, b.ReturnDate, b.IsReturned, l.Name AS Librarian
FROM MediaInstance AS mi, Media AS me, Borrow AS b, Person AS l, LibraryCard AS lc
WHERE mi.InstanceID = b.InstanceID
AND mi.MediaID = me.MediaID
AND b.LibrarianID = l.PersonID
AND b.LibraryCardNo = lc.CardNo
AND lc.CustomerID = customerID;
END;
//
DELIMITER //
CREATE PROCEDURE
searchMedia (searchQuery VARCHAR(255))
BEGIN
SELECT q.MediaID, q.DeweyNumber, q.Name, q.MediaType, q.CreatedYear,
q.Creators, q.Tags, q.Genres, q.Copies, q.AvailableCopies
FROM (
SELECT me.*,
GROUP_CONCAT(DISTINCT(c.name)) AS Creators,
GROUP_CONCAT(DISTINCT(t.name)) AS Tags,
GROUP_CONCAT(DISTINCT(g.name)) AS Genres,
COUNT(DISTINCT(mi.InstanceID)) AS Copies,
COUNT(DISTINCT(mi.InstanceID)) - COUNT(DISTINCT(b.InstanceID)) AS AvailableCopies,
MAX(MATCH (me.Name, me.MediaType, me.DeweyNumber) AGAINST (searchQuery)) AS NameScore,
MAX(MATCH (c.Name) AGAINST (searchQuery)) AS CreatorScore,
MAX(MATCH (t.Name) AGAINST (searchQuery)) AS TagScore,
MAX(MATCH (g.Name) AGAINST (searchQuery)) AS GenreScore
FROM Media AS me
LEFT JOIN MediaInstance AS mi ON me.MediaID = mi.MediaID
LEFT JOIN MediaGenre AS mg ON me.MediaID = mg.MediaID
LEFT JOIN Genre AS g ON mg.GenreID = g.GenreID
LEFT JOIN MediaTag AS mt ON me.MediaID = mt.MediaID
LEFT JOIN Tag AS t ON mt.TagID = t.TagID
LEFT JOIN MediaCreator AS mc ON me.MediaID = mc.MediaID
LEFT JOIN Creator AS c ON mc.CreatorID = c.CreatorID
LEFT JOIN Borrow AS b ON b.InstanceID = mi.InstanceID AND b.IsReturned
GROUP BY me.MediaID) AS q
WHERE NameScore > 0 OR CreatorScore > 0 OR TagScore > 0 OR GenreScore > 0
ORDER BY NameScore DESC, CreatorScore DESC, TagScore DESC, GenreScore DESC, AvailableCopies DESC;
END;
//
DELIMITER //
CREATE PROCEDURE borrow( libraryCardNo INT, mediaID INT, librarianID INT, returnDate DATE, OUT instanceID INT )
BEGIN
DECLARE instanceID INT;
SELECT mi.InstanceID INTO instanceID
FROM MediaInstance AS mi
LEFT JOIN Borrow AS b ON b.InstanceID = mi.InstanceID AND b.IsReturned
WHERE mi.MediaID = mediaID AND b.InstanceID IS NULL
LIMIT 1;
INSERT INTO Borrow (LibraryCardNo, InstanceID, LibrarianID, BorrowDate, ReturnDate, IsReturned)
VALUES (libraryCardNo, instanceID, librarianID, CURDATE(), returnDate, FALSE);
END;
//
|