MVC-databaskonstruktion

Log | Files | Refs | README

dbschema.sql (15310B)


      1 DROP DATABASE IF EXISTS a22willi;
      2 CREATE DATABASE a22willi;
      3 USE a22willi;
      4 
      5 /* --- TABLE DEFINITIONS --- */
      6 
      7 CREATE TABLE Agent (
      8 	CodeName		CHAR(3) UNIQUE NOT NULL,
      9     FirstName		VARCHAR(32) NOT NULL,
     10     LastName		VARCHAR(32) NOT NULL,
     11     Salary			DECIMAL,
     12     IsGroupLeader	BOOL DEFAULT 0,
     13     IsManager		BOOL DEFAULT 0,
     14     IsFieldAgent	BOOL DEFAULT 1,
     15     CHECK 			(CodeName RLIKE '^[a-zA-Z][0-9]{1,2}$'), -- Only accept ex: 'B23' or 'x5'.
     16     CHECK 			(LENGTH(FirstName) > 0 AND LENGTH(LastName) > 0),
     17     CHECK 			(Salary >= 0),
     18     PRIMARY KEY 	(CodeName)
     19 ) ENGINE = INNODB;
     20 
     21 CREATE INDEX Name ON Agent(FirstName, LastName) USING BTREE;
     22 
     23 -- Denormalization (Horizontal split)
     24 CREATE TABLE FieldAgentAttributes (
     25 	AgentCodeName	CHAR(3) UNIQUE NOT NULL, -- Prevent multiple FieldAgentAttributes per Agent using unique.
     26 	Specialty		VARCHAR(256),
     27     Competence		VARCHAR(256),
     28     CHECK 			(Specialty IS NULL OR LENGTH(Specialty) > 0),
     29     CHECK 			(Competence IS NULL OR LENGTH(Competence) > 0),
     30     PRIMARY KEY 	(AgentCodeName),
     31     FOREIGN KEY 	(AgentCodeName) REFERENCES Agent(CodeName)
     32 ) ENGINE = INNODB;
     33 
     34 -- Denormalization (Codes)
     35 CREATE TABLE Terrain (
     36 	TerrainCode		TINYINT UNSIGNED UNIQUE NOT NULL,
     37     TerrainName		VARCHAR(64),
     38     PRIMARY KEY 	(TerrainCode)
     39 ) ENGINE = INNODB;
     40 
     41 CREATE INDEX TerrainTypes ON Terrain(TerrainName);
     42 
     43 -- Denormalization (Merge)
     44 CREATE TABLE Incident (
     45 	RegionName		VARCHAR(32) NOT NULL,
     46     Terrain			TINYINT UNSIGNED NOT NULL,
     47 	IncidentName	VARCHAR(64) NOT NULL,
     48     IncidentNumber	INT UNIQUE NOT NULL,
     49     Location		VARCHAR(128),
     50     PRIMARY KEY 	(IncidentName, IncidentNumber),
     51     FOREIGN KEY 	(Terrain) REFERENCES Terrain(TerrainCode)
     52 ) ENGINE = INNODB;
     53 
     54 -- Denormalization (Vertical split)
     55 CREATE TABLE Report (
     56 	DateCreated 	DATETIME NOT NULL,
     57     Author			CHAR(3) NOT NULL,
     58     Title			VARCHAR(64) NOT NULL,
     59     Content			VARCHAR(1024),
     60     IncidentName	VARCHAR(64),
     61     IncidentNumber	INT,
     62     PRIMARY KEY 	(DateCreated, Title),
     63     FOREIGN KEY		(Author) REFERENCES Agent(CodeName),
     64 	FOREIGN KEY 	(IncidentName, IncidentNumber) REFERENCES Incident(IncidentName, IncidentNumber)
     65 ) ENGINE = INNODB;
     66 
     67 -- Denormalization (Vertical split)
     68 CREATE TABLE ArchivedReport (
     69 	DateCreated 	DATETIME NOT NULL,
     70     Author			CHAR(3) NOT NULL,
     71     Title			VARCHAR(64) NOT NULL DEFAULT 'nameless_report',
     72     Content			VARCHAR(1024),
     73     IncidentName	VARCHAR(64),
     74     IncidentNumber	INT,
     75     PRIMARY KEY 	(DateCreated, Title),
     76 	FOREIGN KEY		(Author) REFERENCES Agent(CodeName),
     77 	FOREIGN KEY 	(IncidentName, IncidentNumber) REFERENCES Incident(IncidentName, IncidentNumber)
     78 ) ENGINE = INNODB;
     79 
     80 CREATE TABLE Operation (
     81 	OperationName	VARCHAR(128) NOT NULL,
     82     StartDate		DATE NOT NULL,
     83     EndDate			DATE,
     84     SuccessRate		BIT,
     85     GroupLeader		CHAR(3),
     86     IncidentName	VARCHAR(64) NOT NULL,
     87     IncidentNumber	INT NOT NULL,
     88     CHECK 			(EndDate IS NULL OR EndDate >= StartDate),
     89 	PRIMARY KEY 	(OperationName, StartDate, IncidentName, IncidentNumber),
     90     FOREIGN KEY 	(IncidentName, IncidentNumber) REFERENCES Incident(IncidentName, IncidentNumber),
     91     FOREIGN KEY 	(GroupLeader) REFERENCES Agent(CodeName)
     92 ) ENGINE = INNODB;
     93 
     94 -- N - M relation (Agent, Operation)
     95 CREATE TABLE OperatesIn (
     96 	IncidentName	VARCHAR(64) NOT NULL,
     97     IncidentNumber	INT NOT NULL,
     98     OperationName	VARCHAR(128) NOT NULL,
     99     StartDate		DATE NOT NULL,
    100     CodeName		CHAR(3) NOT NULL,
    101     PRIMARY KEY 	(IncidentName, IncidentNumber, OperationName, StartDate, CodeName),
    102     FOREIGN KEY 	(IncidentName, IncidentNumber) REFERENCES Incident(IncidentName, IncidentNumber),
    103     FOREIGN KEY 	(OperationName, StartDate) REFERENCES Operation(OperationName, StartDate),
    104     FOREIGN KEY 	(CodeName) REFERENCES Agent(CodeName)
    105 ) ENGINE = INNODB;
    106 
    107 /* --- LOG TABLES --- */
    108 
    109 CREATE TABLE AgentLog (
    110 	Id 				BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    111     Operation		VARCHAR(10),
    112     UserName		VARCHAR(32),
    113     CodeName		CHAR(3),
    114     OperationTime	DATETIME,
    115     PRIMARY KEY 	(Id)
    116 ) ENGINE = INNODB;
    117 
    118 /* --- STORED PROCEDURES --- */
    119 
    120 DELIMITER //
    121 
    122 -- Get the number of active operations an agent has. 
    123 -- Conditional procedure with error handling
    124 CREATE PROCEDURE GetNumberOfActiveOperations(IN _Agent CHAR(3), OUT _NumOfActiveOperations SMALLINT UNSIGNED)
    125 BEGIN
    126 	IF (SELECT COUNT(*) FROM Agent WHERE CodeName = _Agent) = 0
    127 		THEN SET @message = CONCAT('Agent: ', _Agent, ' does not exist.');
    128 		SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = @message;
    129 	END IF;
    130     
    131 	SELECT COUNT(OpIn.OperationName) AS SumOfOperations INTO _NumOfActiveOperations 
    132 	FROM OperatesIn OpIn
    133     JOIN Operation Op ON OpIn.OperationName = Op.OperationName AND OpIn.StartDate = Op.StartDate
    134     WHERE OpIn.CodeName = _Agent AND (Op.EndDate IS NULL OR Op.EndDate > CURDATE());
    135 END//
    136 
    137 -- Simple procedure
    138 CREATE PROCEDURE GetAgentNamesAlphabetically()
    139 BEGIN
    140 	SELECT FirstName, LastName, CodeName
    141     FROM Agent
    142     ORDER BY LastName, FirstName;
    143 END//
    144 
    145 CREATE PROCEDURE GetOperationRegion(IN _OpName VARCHAR(128), OUT _Region VARCHAR(32))
    146 BEGIN
    147 	SELECT Inc.RegionName INTO _Region
    148     FROM Incident Inc
    149     JOIN Operation Op ON Op.IncidentName = Inc.IncidentName AND Op.IncidentNumber = Inc.IncidentNumber
    150     WHERE Op.OperationName = _OpName;
    151 END//
    152 
    153 -- Move report from Report to ArchivedReport
    154 -- Procedure that moves tuple in vertical split
    155 CREATE PROCEDURE ArchiveReport(IN _DateCreated DATETIME, IN _Title VARCHAR(64))
    156 BEGIN
    157     INSERT INTO ArchivedReport (DateCreated, Author, Title, Content, IncidentName, IncidentNumber) 
    158     SELECT R.DateCreated, R.Author, R.Title, R.Content, R.IncidentName, R.IncidentNumber
    159     FROM Report R 
    160     WHERE R.DateCreated = _DateCreated AND R.Title = _Title;
    161     
    162     DELETE FROM Report
    163     WHERE Report.DateCreated = _DateCreated AND Report.Title = _Title;
    164 END //
    165 
    166 -- get Operations between two dates
    167 -- procedure that selects data
    168 CREATE PROCEDURE GetOperationsInRange(IN _StartDate DATE, IN _EndDate DATE)
    169 BEGIN 
    170 	SELECT * 
    171     FROM Operation Op
    172     WHERE Op.StartDate > _StartDate 
    173     AND Op.EndDate IS NOT NULL AND Op.EndDate < _EndDate;
    174 END//
    175 
    176 /* --- TRIGGERS --- */
    177 
    178 -- Log Agent operations
    179 CREATE TRIGGER AgentLogInsert AFTER INSERT ON Agent
    180 FOR EACH ROW BEGIN
    181 	INSERT INTO AgentLog (Operation, UserName, CodeName, OperationTime) VALUES
    182     ('INSERT', USER(), NEW.CodeName, NOW());
    183 END//
    184 
    185 CREATE TRIGGER AgentLogUpdate AFTER UPDATE ON Agent
    186 FOR EACH ROW BEGIN
    187 	INSERT INTO AgentLog (Operation, UserName, CodeName, OperationTime) VALUES
    188     ('UPDATE', USER(), NEW.CodeName, NOW());
    189 END//
    190 
    191 CREATE TRIGGER AgentLogDelete AFTER DELETE ON Agent
    192 FOR EACH ROW BEGIN
    193 	INSERT INTO AgentLog (Operation, UserName, CodeName, OperationTime) VALUES
    194     ('DELETE', USER(), OLD.CodeName, NOW());
    195 END//
    196 
    197 -- Validate that an agent is part of at least one group, fieldagents, managers or groupleaders.
    198 CREATE TRIGGER ValidateAgent BEFORE INSERT ON Agent
    199 FOR EACH ROW BEGIN
    200 	IF NEW.IsGroupLeader = 0 AND NEW.IsManager = 0 AND NEW.IsFieldAgent = 0 
    201 		THEN 
    202 			SIGNAL SQLSTATE '45000'
    203 			SET MESSAGE_TEXT = 'Agent missing type (eg: FieldAgent).';
    204     END IF;
    205 END//
    206 
    207 -- Before deleting an incident, also remove all related operations and reports. 
    208 CREATE TRIGGER IncidentCleanup BEFORE DELETE ON Incident
    209 FOR EACH ROW BEGIN
    210     DELETE FROM Report
    211     WHERE IncidentName = OLD.IncidentName AND IncidentNumber = OLD.IncidentNumber;
    212     
    213     DELETE FROM ArchivedReport
    214     WHERE IncidentName = OLD.IncidentName AND IncidentNumber = OLD.IncidentNumber;
    215     
    216     DELETE FROM OperatesIn
    217     WHERE IncidentName = OLD.IncidentName AND IncidentNumber = OLD.IncidentNumber;
    218     
    219     DELETE FROM Operation
    220     WHERE IncidentName = OLD.IncidentName AND IncidentNumber = OLD.IncidentNumber;
    221 END//
    222 
    223 -- Validate that an agent can only be part of Operations in the same region, 
    224 -- And at most 3 operations in the same region. 
    225 CREATE TRIGGER ValidateOperatesIn BEFORE INSERT ON OperatesIn
    226 FOR EACH ROW BEGIN
    227 	DECLARE MaxAllowedOperations INT DEFAULT 3;
    228 	DECLARE ExistingRegion VARCHAR(32) DEFAULT NULL;
    229     DECLARE NewRegion VARCHAR(32) DEFAULT NULL;
    230     DECLARE NumOfOperations SMALLINT UNSIGNED DEFAULT NULL;
    231 
    232     -- Get the region of the new operation the agent is being added to
    233     CALL GetOperationRegion(NEW.OperationName, NewRegion);
    234     
    235     -- Get the current number of active operations an agent has
    236     CALL GetNumberOfActiveOperations(NEW.CodeName, NumOfOperations);
    237 
    238     -- Get the region of an existing operation the agent is part of
    239     SELECT Inc.RegionName INTO ExistingRegion
    240     FROM Incident Inc, Operation Op, OperatesIn OpIn
    241     WHERE Op.OperationName = OpIn.OperationName AND Op.StartDate = OpIn.StartDate AND Op.IncidentName = Inc.IncidentName AND Op.IncidentNumber = Inc.IncidentNumber
    242     AND OpIn.CodeName = NEW.CodeName AND (Op.EndDate IS NULL OR Op.EndDate > CURDATE())
    243     LIMIT 1;
    244 
    245     -- If the agent is part of an operation in a different region, raise an error
    246     IF ExistingRegion IS NOT NULL AND ExistingRegion != NewRegion THEN
    247 		SET @message = CONCAT('Agent already has ongoing operation in region:', ExistingRegion);
    248         SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = @message;
    249 	END IF;
    250     
    251     -- If the agent is part already part of 3 operations, raise an error
    252 	IF NumOfOperations >= MaxAllowedOperations THEN 
    253 		SIGNAL SQLSTATE '45000'
    254         SET MESSAGE_TEXT = 'Agent can be part of no more than 3 operations in the same region.';
    255     END IF;
    256 END//
    257 
    258 CREATE TRIGGER ValidateOperation BEFORE INSERT ON Operation
    259 FOR EACH ROW
    260 BEGIN
    261     IF NEW.EndDate > DATE_ADD(NEW.StartDate, INTERVAL 5 WEEK) THEN
    262         SET NEW.EndDate = DATE_ADD(NEW.StartDate, INTERVAL 5 WEEK);
    263     END IF;
    264 END//
    265 
    266 
    267 DELIMITER ;
    268 
    269 /* --- VIEWS --- */
    270 
    271 -- Specialization
    272 -- Get the average successrate for all agents
    273 CREATE VIEW AgentRates AS
    274 SELECT 
    275     OpIn.CodeName,
    276     AVG(Op.SuccessRate) AS AvgSuccessRate
    277 FROM OperatesIn OpIn, Operation Op
    278 WHERE 
    279     OpIn.OperationName = Op.OperationName
    280     AND OpIn.StartDate = Op.StartDate
    281 GROUP BY OpIn.CodeName;
    282 
    283 -- Simplification (and specialization)
    284 -- Get all field agents, including their competence, specialization and average successrate.
    285 CREATE VIEW FieldAgents AS
    286 SELECT Agent.CodeName, Firstname, LastName, Salary, Specialty, Competence, AvgSuccessRate
    287 FROM Agent, FieldAgentAttributes, AgentRates
    288 WHERE Agent.IsFieldAgent
    289 AND Agent.CodeName = FieldAgentAttributes.AgentCodeName
    290 AND Agent.CodeName = AgentRates.CodeName;
    291 
    292 -- Simplification
    293 -- Get all GroupLeaders
    294 CREATE VIEW GroupLeaders AS
    295 SELECT Agent.CodeName, FirstName, LastName, Salary, AvgSuccessRate
    296 FROM Agent, AgentRates
    297 WHERE IsGroupLeader
    298 AND Agent.CodeName = AgentRates.CodeName;
    299 
    300 -- Simplification
    301 -- Get all managers
    302 CREATE VIEW Managers AS
    303 SELECT Agent.CodeName, FirstName, LastName, Salary, AvgSuccessRate
    304 FROM Agent, AgentRates
    305 WHERE IsManager
    306 AND Agent.CodeName = AgentRates.CodeName;
    307 
    308 -- Simplification
    309 CREATE VIEW AllReports AS
    310 SELECT * 
    311 FROM Report
    312 UNION ALL
    313 SELECT *
    314 FROM ArchivedReport;
    315 
    316 CREATE VIEW AgentOperations AS
    317 SELECT A.CodeName, OpIn.OperationName
    318 FROM Agent A, OperatesIn OpIn
    319 WHERE A.CodeName = OpIn.CodeName;
    320 
    321 /* --- GENERATE MOCK DATA --- */
    322 
    323 INSERT INTO Terrain (TerrainName, TerrainCode) VALUES 
    324 	('Mountainous', 1),
    325 	('Plains', 2),
    326 	('Hills', 3),
    327 	('Desert', 4),
    328 	('Forest', 5);
    329 
    330 INSERT INTO Agent (CodeName, FirstName, LastName, Salary, IsGroupLeader, IsManager, IsFieldAgent) VALUES
    331 	('A1', 'John', 'Doe', 50000, TRUE, FALSE, TRUE),
    332 	('B23', 'Jane', 'Smith', 52000, FALSE, TRUE, FALSE),
    333 	('C45', 'Alex', 'Johnson', 51000, FALSE, FALSE, TRUE),
    334 	('D56', 'Emily', 'Brown', 50500, TRUE, FALSE, FALSE),
    335 	('E78', 'Michael', 'Davis', 53000, FALSE, TRUE, TRUE);
    336 
    337 INSERT INTO Incident (RegionName, Terrain, IncidentName, IncidentNumber, Location) VALUES
    338 	('North', 1, 'Heist', 101, 'North Town'),
    339 	('West', 2, 'Kidnapping', 102, 'South City'),
    340 	('North', 3, 'Sabotage', 103, 'East Village'),
    341 	('South', 4, 'Espionage', 104, 'West Point'),
    342 	('NorthEast', 3, 'Breach', 105, 'Central Hub'),
    343     ('SouthWest', 2, 'Ambush', 106, 'Mountain Pass'),
    344 	('MidWest', 4, 'Hijacking', 107, 'River Crossing'),
    345 	('Central', 1, 'Arson', 108, 'Forest Edge'),
    346 	('OuterWest', 3, 'Assault', 109, 'Desert Base'),
    347 	('DeepSouth', 2, 'Theft', 110, 'Island Shore');
    348 
    349 INSERT INTO Operation (OperationName, StartDate, EndDate, SuccessRate, GroupLeader, IncidentName, IncidentNumber) VALUES
    350 	('Operation Thunder', '2023-01-01', '2023-10-18', 1, 'A1', 'Sabotage', 103),
    351     ('Operation QuickSilver1', '2023-02-05', '2023-02-18', 0, 'D56', 'Heist', 101),
    352     ('Operation QuickSilver2', '2023-02-05', '2025-10-18', 0, 'D56', 'Heist', 101),
    353     ('Operation QuickSilver3', '2023-02-05', '2023-11-18', 0, 'D56', 'Heist', 101),
    354     ('Operation NightFall', '2023-03-10', '2023-03-30', 1, 'A1', 'Sabotage', 103),
    355     ('Operation DesertStorm', '2023-04-01', NULL, 1, 'E78', 'Espionage', 104),
    356     ('Operation ForestGuard', '2023-05-01', '2023-05-10', NULL, 'D56', 'Breach', 105);
    357 
    358 INSERT INTO OperatesIn (IncidentName, IncidentNumber, OperationName, StartDate, CodeName) VALUES
    359 	('Heist', 101, 'Operation Thunder', '2023-01-01', 'A1'),
    360 	('Heist', 101, 'Operation Thunder', '2023-01-01', 'B23'),
    361 	('Kidnapping', 102, 'Operation QuickSilver1', '2023-02-05', 'B23'),
    362 	('Kidnapping', 102, 'Operation QuickSilver2', '2023-02-05', 'B23'),
    363 	('Kidnapping', 102, 'Operation QuickSilver3', '2023-02-05', 'B23'),
    364 	('Kidnapping', 102, 'Operation QuickSilver1', '2023-02-05', 'C45'),
    365 	('Espionage', 104, 'Operation DesertStorm', '2023-04-01', 'D56'),
    366 	('Breach', 105, 'Operation ForestGuard', '2023-05-01', 'E78');
    367     
    368 INSERT INTO FieldAgentAttributes (AgentCodeName, Specialty, Competence) VALUES 
    369     ('A1', 'Explosives Expert', 'Advanced explosives handling and defusal'),
    370     ('C45', 'Surveillance Specialist', 'Expert in covert surveillance and intelligence gathering'),
    371     ('E78', 'Combat Specialist', 'Skilled hand-to-hand combat and small arms use');
    372     
    373 INSERT INTO Report (DateCreated, Author, Title, Content, IncidentName, IncidentNumber) VALUES
    374     ('2023-04-15', 'A1', 'Update on Heist', 'Latest details on the heist operation. Successful with minor hitches.', 'Heist', 101),
    375     ('2023-06-10', 'B23', 'Kidnapping in West', 'The kidnapping incident at South City has been contained.', 'Kidnapping', 102),
    376     ('2023-06-20', 'D56', 'Sabotage Alert', 'Sabotage in East Village has caused significant infrastructure damage.', 'Sabotage', 103),
    377     ('2023-07-01', 'A1', 'Espionage in South', 'Espionage activities spotted in West Point. Suspects are being tracked.', 'Espionage', 104),
    378     ('2023-08-15', 'C45','Breach in NorthEast', 'A major breach in Central Hub. Cybersecurity teams are on it.', 'Breach', 105);
    379 
    380 INSERT INTO ArchivedReport (DateCreated, Author, Title, Content, IncidentName, IncidentNumber) VALUES
    381     ('2020-03-05', 'D56', 'Old Heist Report', 'Details on an old heist operation.', 'Heist', 101),
    382     ('2019-12-10', 'A1', 'Old Kidnapping Details', 'Report on a kidnapping that happened two years back.', 'Kidnapping', 102),
    383     ('2021-01-20', 'B23', 'Sabotage in East - Historical', 'An old sabotage report detailing the damage.', 'Sabotage', 103),
    384     ('2021-02-15', 'A1', 'Espionage History', 'Previous espionage activities in West Point.', 'Espionage', 104),
    385     ('2021-04-05', 'E78', 'Breach Report - Historical', 'A significant breach happened two years back in Central Hub.', 'Breach', 105);