Files
EinsatzplanQT/script.sql
2024-07-09 01:19:22 +02:00

355 lines
13 KiB
PL/PgSQL

CREATE SEQUENCE global_id_seq START WITH 1000000 INCREMENT BY 1;
CREATE OR REPLACE FUNCTION random_between_two()
RETURNS VARCHAR AS $$
BEGIN
IF random() < 0.5 THEN
RETURN 'A';
ELSE
RETURN 'B';
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION random_between_days()
RETURNS INTEGER AS $$
BEGIN
RETURN floor(random() * 5 + 1)::INTEGER;
END;
$$ LANGUAGE plpgsql;
CREATE TABLE IF NOT EXISTS Uhrzeit (
ID SERIAL PRIMARY KEY,
anfangszeit TIME NOT NULL,
endzeit TIME NOT NULL
);
CREATE TABLE IF NOT EXISTS Veranstaltung(
name VARCHAR(3) PRIMARY KEY,
ort VARCHAR(1) DEFAULT random_between_two(),
raum INTEGER NOT NULL,
dauer INTEGER NOT NULL,
used_in_plan INTEGER DEFAULT 0
);
CREATE TABLE IF NOT EXISTS Veranstalter(
ID INTEGER PRIMARY KEY DEFAULT nextval('global_id_seq'),
name VARCHAR(30),
email VARCHAR(30),
passwort VARCHAR(30),
arbeitszeit INTEGER DEFAULT 0,
admin BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE TABLE IF NOT EXISTS StundeImPlan (
uhrzeit INTEGER REFERENCES Uhrzeit(ID),
tag INTEGER NOT NULL,
veranstaltung VARCHAR(3) REFERENCES Veranstaltung(name) ON DELETE SET NULL,
veranstalter INTEGER REFERENCES Veranstalter(ID) ON DELETE SET NULL,
PRIMARY KEY(uhrzeit, tag)
);
CREATE TABLE IF NOT EXISTS Krank(
ID SERIAL PRIMARY KEY,
stundeImPlan_uhrzeit INTEGER,
stundeImPlan_tag INTEGER,
veranstalter INTEGER REFERENCES Veranstalter(ID),
krank BOOLEAN DEFAULT FALSE,
FOREIGN KEY (stundeImPlan_uhrzeit, stundeImPlan_tag) REFERENCES StundeImPlan(uhrzeit, tag)
);
-- Function to delete StundeImPlan when Veranstaltung is deleted
CREATE OR REPLACE FUNCTION delete_stundeimplan_for_veranstaltung()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM StundeImPlan WHERE veranstaltung = OLD.name;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_delete_stundeimplan_for_veranstaltung
AFTER DELETE ON Veranstaltung
FOR EACH ROW
EXECUTE FUNCTION delete_stundeimplan_for_veranstaltung();
CREATE OR REPLACE FUNCTION update_stundeimplan_for_veranstalter()
RETURNS TRIGGER AS $$
DECLARE
new_veranstalter_id INTEGER;
BEGIN
SELECT ID INTO new_veranstalter_id
FROM Veranstalter
WHERE arbeitszeit < 18
ORDER BY arbeitszeit ASC
LIMIT 1;
IF new_veranstalter_id IS NOT NULL THEN
UPDATE StundeImPlan
SET veranstalter = new_veranstalter_id
WHERE veranstalter = OLD.ID;
END IF;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
-- Trigger to call the above function
CREATE TRIGGER trg_update_stundeimplan_for_veranstalter
AFTER DELETE ON Veranstalter
FOR EACH ROW
EXECUTE FUNCTION update_stundeimplan_for_veranstalter();
-- Function to update arbeitszeit of Veranstalter when assigned to StundeImPlan
CREATE OR REPLACE FUNCTION update_arbeitszeit_for_veranstalter()
RETURNS TRIGGER AS $$
BEGIN
UPDATE Veranstalter
SET arbeitszeit = arbeitszeit + (SELECT dauer FROM Veranstaltung WHERE name = NEW.veranstaltung)
WHERE ID = NEW.veranstalter;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Trigger to call the above function
CREATE TRIGGER trg_update_arbeitszeit_for_veranstalter
AFTER INSERT ON StundeImPlan
FOR EACH ROW
EXECUTE FUNCTION update_arbeitszeit_for_veranstalter();
-- Function to generate the plan
CREATE OR REPLACE FUNCTION generate_plan()
RETURNS TRIGGER AS $$
DECLARE
v_row RECORD;
u_row RECORD;
available_veranstalter RECORD;
random_day INTEGER;
BEGIN
-- Loop through each available time slot
FOR u_row IN SELECT * FROM Uhrzeit LOOP
-- Loop through each available Veranstaltung
FOR v_row IN SELECT * FROM Veranstaltung WHERE used_in_plan = 0 LOOP
-- Find an available Veranstalter with matching ort and not scheduled in the same tag and uhrzeit
SELECT * INTO available_veranstalter
FROM Veranstalter v
WHERE v.arbeitszeit + v_row.dauer <= 18
AND NOT EXISTS (
SELECT 1
FROM StundeImPlan sp
JOIN Veranstaltung va ON sp.veranstaltung = va.name
WHERE sp.uhrzeit = u_row.ID
AND sp.tag = random_day
AND sp.veranstalter = v.ID
)
AND NOT EXISTS (
SELECT 1
FROM StundeImPlan prev_sp
JOIN Veranstaltung prev_va ON prev_sp.veranstaltung = prev_va.name
WHERE prev_sp.uhrzeit = u_row.ID - 1
AND prev_sp.tag = random_day
AND prev_sp.veranstalter = v.ID
AND prev_va.ort <> v_row.ort -- Ensure different ort for previous hour
)
ORDER BY v.arbeitszeit ASC
LIMIT 1;
IF FOUND THEN
-- Check if there is already an event scheduled for this tag and uhrzeit combination
random_day = random_between_days();
IF NOT EXISTS (
SELECT 1
FROM StundeImPlan
WHERE uhrzeit = u_row.ID AND tag = random_day
) THEN
-- If an available Veranstalter is found, insert into StundeImPlan
INSERT INTO StundeImPlan (uhrzeit, tag, veranstaltung, veranstalter)
VALUES (u_row.ID, random_day, v_row.name, available_veranstalter.ID);
-- Update the used_in_plan flag and arbeitszeit
UPDATE Veranstaltung SET used_in_plan = used_in_plan + 1 WHERE name = v_row.name;
UPDATE Veranstalter SET arbeitszeit = arbeitszeit + v_row.dauer WHERE ID = available_veranstalter.ID;
-- If the duration is 4, also insert for the next time slot
IF v_row.dauer = 4 THEN
-- Ensure the next time slot is within the range
IF EXISTS (SELECT 1 FROM Uhrzeit WHERE ID = u_row.ID + 1) THEN
-- Check if the next time slot is free
IF NOT EXISTS (
SELECT 1
FROM StundeImPlan
WHERE uhrzeit = u_row.ID + 1 AND tag = random_day
) THEN
INSERT INTO StundeImPlan (uhrzeit, tag, veranstaltung, veranstalter)
VALUES (u_row.ID + 1, random_day, v_row.name, available_veranstalter.ID);
-- Mark the second part as used in the plan
UPDATE Veranstaltung SET used_in_plan = used_in_plan + 1 WHERE name = v_row.name;
END IF;
END IF;
END IF;
END IF;
END IF;
END LOOP;
END LOOP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_generate_plan
AFTER INSERT ON Veranstaltung
FOR EACH STATEMENT
EXECUTE FUNCTION generate_plan();
CREATE OR REPLACE FUNCTION handle_krank_insert()
RETURNS TRIGGER AS $$
DECLARE
new_veranstalter_id INTEGER;
v_name VARCHAR(3);
BEGIN
-- Find the name of the Veranstaltung for the same stundeImPlan
SELECT veranstaltung.name INTO v_name
FROM Veranstaltung
WHERE name = (SELECT veranstaltung FROM StundeImPlan WHERE uhrzeit = NEW.stundeImPlan_uhrzeit AND tag = NEW.stundeImPlan_tag);
WITH PreviousHour AS (
SELECT sp.veranstalter, v.ort
FROM StundeImPlan sp
JOIN Veranstaltung v ON sp.veranstaltung = v.name
WHERE sp.uhrzeit = NEW.stundeImPlan_uhrzeit - 1
AND sp.tag = NEW.stundeImPlan_tag
)
SELECT v.ID INTO new_veranstalter_id
FROM Veranstalter v
JOIN StundeImPlan sp ON v.ID = sp.veranstalter
JOIN Veranstaltung va ON sp.veranstaltung = va.name
LEFT JOIN PreviousHour ph ON v.ID = ph.veranstalter
WHERE v.arbeitszeit + (SELECT dauer FROM Veranstaltung WHERE name = v_name) <= 18
AND NOT EXISTS (
SELECT 1
FROM Krank k
WHERE k.veranstalter = v.ID
AND k.stundeImPlan_uhrzeit = NEW.stundeImPlan_uhrzeit
AND k.stundeImPlan_tag = NEW.stundeImPlan_tag
)
AND (ph.veranstalter IS NULL OR ph.veranstalter <> (
SELECT v.ID
FROM Veranstalter v
JOIN StundeImPlan sp ON v.ID = sp.veranstalter
JOIN Veranstaltung vaa ON sp.veranstaltung = vaa.name
WHERE sp.uhrzeit = NEW.stundeImPlan_uhrzeit
AND sp.tag = NEW.stundeImPlan_tag
AND vaa.ort = va.ort
))
ORDER BY v.arbeitszeit ASC
LIMIT 1;
-- Check if a new veranstalter was found
IF new_veranstalter_id IS NOT NULL THEN
UPDATE Veranstalter
SET arbeitszeit = arbeitszeit + (SELECT dauer FROM Veranstaltung WHERE name = v_name)
WHERE ID = new_veranstalter_id;
-- Update the StundeImPlan with the replacement
UPDATE StundeImPlan
SET veranstalter = new_veranstalter_id
WHERE uhrzeit = NEW.stundeImPlan_uhrzeit AND tag = NEW.stundeImPlan_tag;
ELSE
-- If no suitable veranstalter is found, set veranstalter to NULL
UPDATE StundeImPlan
SET veranstalter = NULL
WHERE uhrzeit = NEW.stundeImPlan_uhrzeit AND tag = NEW.stundeImPlan_tag;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_handle_krank_insert
AFTER INSERT ON Krank
FOR EACH ROW
EXECUTE FUNCTION handle_krank_insert();
CREATE OR REPLACE FUNCTION handle_veranstalter_deletion() RETURNS TRIGGER AS $$
DECLARE
new_veranstalter RECORD;
affected_row RECORD;
BEGIN
FOR affected_row IN
SELECT * FROM StundeImPlan WHERE veranstalter = OLD.id
LOOP
-- Find a new suitable Veranstalter
SELECT * INTO new_veranstalter
FROM Veranstalter v
WHERE v.arbeitszeit + (SELECT dauer FROM Veranstaltung va WHERE va.name = affected_row.veranstaltung) <= 18
AND NOT EXISTS (
SELECT 1
FROM Krank k
WHERE k.veranstalter = v.ID
AND k.stundeImPlan_uhrzeit = affected_row.uhrzeit
AND k.stundeImPlan_tag = affected_row.tag
)
AND NOT EXISTS (
SELECT 1
FROM StundeImPlan sp
JOIN Veranstaltung va ON sp.veranstaltung = va.name
WHERE sp.uhrzeit = affected_row.uhrzeit - 1
AND sp.tag = affected_row.tag
AND sp.veranstalter = v.ID
AND va.ort <> (SELECT ort FROM Veranstaltung WHERE name = affected_row.veranstaltung)
)
AND v.ID <> affected_row.veranstalter
ORDER BY v.arbeitszeit ASC
LIMIT 1;
UPDATE StundeImPlan
SET veranstalter = new_veranstalter.ID
WHERE uhrzeit = affected_row.uhrzeit AND tag = affected_row.tag AND veranstalter = OLD.id;
UPDATE Veranstalter
SET arbeitszeit = arbeitszeit + (SELECT dauer FROM Veranstaltung WHERE name = affected_row.veranstaltung)
WHERE ID = new_veranstalter.ID;
END LOOP;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_veranstalter_delete
BEFORE DELETE ON Veranstalter
FOR EACH ROW
EXECUTE FUNCTION handle_veranstalter_deletion();
INSERT INTO Uhrzeit (anfangszeit, endzeit) VALUES
('08:00:00', '10:00:00'),
('10:00:00', '12:00:00'),
('12:00:00', '14:00:00'),
('14:00:00', '16:00:00'),
('16:00:00', '18:00:00');
INSERT INTO Veranstalter (name, email, passwort, admin) VALUES
('Davids', 'admin@example.com', 'password123', TRUE),
('Dalitz', 'user1@example.com', 'password1', FALSE),
('Tipp', 'user2@example.com', 'password2', FALSE),
('Quix', 'user3@example.com', 'password3', FALSE),
('Nietsche', 'user4@example.com', 'password4', FALSE),
('Ueberholz', 'user5@example.com', 'password5', FALSE),
('Rethmann', 'user6@example.com', 'password6', FALSE),
('Pohle-Fröhlich', 'user7@example.com', 'password7', FALSE),
('Stockmann', 'user8@example.com', 'password8', FALSE),
('Gref', 'user9@example.com', 'password9', FALSE),
('Naroska', 'user10@example.com', 'password10', FALSE),
('Grothe', 'user11@example.com', 'supersicherespasswort123', FALSE);
INSERT INTO Veranstaltung (ort, raum, name, dauer) VALUES
('A', '101', 'GDI', 2),
('B', '202', 'ALD', 4),
('A', '103', 'ITS', 2),
('B', '204', 'BSY', 4),
('A', '105', 'PE1', 2),
('B', '206', 'THI', 4),
('A', '107', 'DBS', 2),
('B', '208', 'WEB', 2),
('A', '109', 'BVA', 2),
('B', '210', 'MA1', 2);