|
|
-- 创建数据库
|
|
|
|
|
|
-- 创建数据表
|
|
|
CREATE TABLE admin (
|
|
|
admin_name VARCHAR(255) PRIMARY KEY,
|
|
|
password VARCHAR(255) NOT NULL
|
|
|
);
|
|
|
|
|
|
CREATE TABLE account (
|
|
|
account_id SERIAL PRIMARY KEY,
|
|
|
type VARCHAR(50),
|
|
|
status VARCHAR(50),
|
|
|
created_by VARCHAR(255) REFERENCES admin(admin_name)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE venue (
|
|
|
venue_id SERIAL PRIMARY KEY,
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
capacity INT NOT NULL CHECK (capacity > 0)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE student (
|
|
|
student_id VARCHAR(255) PRIMARY KEY,
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
password VARCHAR(255) NOT NULL,
|
|
|
personal_info_id INT UNIQUE,
|
|
|
FOREIGN KEY (personal_info_id) REFERENCES personal_info(info_id)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE coach (
|
|
|
coach_id VARCHAR(255) PRIMARY KEY,
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
password VARCHAR(255) NOT NULL,
|
|
|
personal_info_id INT UNIQUE,
|
|
|
FOREIGN KEY (personal_info_id) REFERENCES personal_info(info_id)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE appointment (
|
|
|
appointment_id SERIAL PRIMARY KEY,
|
|
|
student_id VARCHAR(255) NOT NULL REFERENCES student(student_id),
|
|
|
coach_id VARCHAR(255) NOT NULL REFERENCES coach(coach_id),
|
|
|
venue_id INT NOT NULL REFERENCES venue(venue_id),
|
|
|
period VARCHAR(50) NOT NULL,
|
|
|
status VARCHAR(50) NOT NULL,
|
|
|
feedback_id INT REFERENCES feedback(feedback_id)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE feedback (
|
|
|
feedback_id SERIAL PRIMARY KEY,
|
|
|
content TEXT NOT NULL,
|
|
|
date DATE NOT NULL
|
|
|
);
|
|
|
|
|
|
CREATE TABLE invoice (
|
|
|
invoice_id SERIAL PRIMARY KEY,
|
|
|
amount DECIMAL(10, 2) NOT NULL CHECK (amount >= 0),
|
|
|
date DATE NOT NULL,
|
|
|
appointment_id INT REFERENCES appointment(appointment_id)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE personal_info (
|
|
|
info_id SERIAL PRIMARY KEY,
|
|
|
student_id VARCHAR(255) NOT NULL REFERENCES student(student_id),
|
|
|
phone VARCHAR(20),
|
|
|
email VARCHAR(255)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE training_record (
|
|
|
record_id SERIAL PRIMARY KEY,
|
|
|
student_id VARCHAR(255) NOT NULL REFERENCES student(student_id),
|
|
|
coach_id VARCHAR(255) NOT NULL REFERENCES coach(coach_id),
|
|
|
feedback TEXT,
|
|
|
appointment_id INT REFERENCES appointment(appointment_id)
|
|
|
);
|
|
|
|
|
|
CREATE TABLE booking_slot (
|
|
|
slot_id SERIAL PRIMARY KEY,
|
|
|
start_time TIME NOT NULL,
|
|
|
end_time TIME NOT NULL CHECK (end_time > start_time),
|
|
|
venue_id INT NOT NULL REFERENCES venue(venue_id)
|
|
|
);
|
|
|
-- 初始化ADMIN表
|
|
|
INSERT INTO admin (admin_name, password) VALUES ('admin1', 'password1');
|
|
|
INSERT INTO admin (admin_name, password) VALUES ('admin2', 'password2');
|
|
|
|
|
|
-- 初始化ACCOUNT表
|
|
|
INSERT INTO account (type, status, created_by) VALUES ('student', 'active', 'admin1');
|
|
|
INSERT INTO account (type, status, created_by) VALUES ('coach', 'active', 'admin1');
|
|
|
|
|
|
-- 初始化VENUE表
|
|
|
INSERT INTO venue (name, capacity) VALUES ('Venue A', 10);
|
|
|
INSERT INTO venue (name, capacity) VALUES ('Venue B', 15);
|
|
|
|
|
|
-- 初始化STUDENT表
|
|
|
INSERT INTO personal_info (phone, email) VALUES ('1234567890', 'student1@example.com');
|
|
|
INSERT INTO student (student_id, name, password, personal_info_id) VALUES ('S001', 'Student One', 'pass1', 1);
|
|
|
|
|
|
-- 初始化COACH表
|
|
|
INSERT INTO personal_info (phone, email) VALUES ('0987654321', 'coach1@example.com');
|
|
|
INSERT INTO coach (coach_id, name, password, personal_info_id) VALUES ('C001', 'Coach One', 'pass1', 2);
|
|
|
|
|
|
-- 初始化APPOINTMENT表
|
|
|
INSERT INTO appointment (student_id, coach_id, venue_id, period, status) VALUES ('S001', 'C001', 1, 'Morning', 'Scheduled');
|
|
|
|
|
|
-- 初始化FEEDBACK表
|
|
|
INSERT INTO feedback (content, date) VALUES ('Great session!', '2024-12-01');
|
|
|
|
|
|
-- 初始化INVOICE表
|
|
|
INSERT INTO invoice (amount, date, appointment_id) VALUES (100.00, '2024-12-01', 1);
|
|
|
|
|
|
-- 初始化TRAINING_RECORD表
|
|
|
INSERT INTO training_record (student_id, coach_id, feedback, appointment_id) VALUES ('S001', 'C001', 'Good progress.', 1);
|
|
|
|
|
|
-- 初始化BOOKING_SLOT表
|
|
|
INSERT INTO booking_slot (start_time, end_time, venue_id) VALUES ('09:00:00', '10:00:00', 1);
|
|
|
-- 学生信息视图
|
|
|
CREATE VIEW StudentInfoView AS
|
|
|
SELECT s.student_id, s.name, p.phone, p.email
|
|
|
FROM student s
|
|
|
JOIN personal_info p ON s.personal_info_id = p.info_id;
|
|
|
|
|
|
-- 教练信息视图
|
|
|
CREATE VIEW CoachInfoView AS
|
|
|
SELECT c.coach_id, c.name, p.phone, p.email
|
|
|
FROM coach c
|
|
|
JOIN personal_info p ON c.personal_info_id = p.info_id;
|
|
|
|
|
|
-- 预约详情视图
|
|
|
CREATE VIEW AppointmentDetailView AS
|
|
|
SELECT a.appointment_id, s.name AS student_name, c.name AS coach_name, v.name AS venue_name, a.period, a.status
|
|
|
FROM appointment a
|
|
|
JOIN student s ON a.student_id = s.student_id
|
|
|
JOIN coach c ON a.coach_id = c.coach_id
|
|
|
JOIN venue v ON a.venue_id = v.venue_id;
|
|
|
|
|
|
-- 训练记录视图
|
|
|
CREATE VIEW TrainingRecordView AS
|
|
|
SELECT t.record_id, s.name AS student_name, c.name AS coach_name, t.feedback
|
|
|
FROM training_record t
|
|
|
JOIN student s ON t.student_id = s.student_id
|
|
|
JOIN coach c ON t.coach_id = c.coach_id;
|
|
|
|
|
|
-- 发票详情视图
|
|
|
CREATE VIEW InvoiceDetailView AS
|
|
|
SELECT i.invoice_id, i.amount, i.date, a.student_id, a.coach_id, a.venue_id
|
|
|
FROM invoice i
|
|
|
JOIN appointment a ON i.appointment_id = a.appointment_id;
|
|
|
-- 在appointment表的period字段上创建索引
|
|
|
CREATE INDEX idx_appointment_period ON appointment(period);
|
|
|
|
|
|
-- 在feedback表的date字段上创建索引
|
|
|
CREATE INDEX idx_feedback_date ON feedback(date);
|
|
|
|
|
|
-- 在invoice表的date字段上创建索引
|
|
|
CREATE INDEX idx_invoice_date ON invoice(date);
|
|
|
|
|
|
-- 在training_record表的record_id字段上创建索引
|
|
|
CREATE INDEX idx_training_record_record_id ON training_record(record_id);
|
|
|
|
|
|
-- 在booking_slot表的start_time和end_time字段上创建复合索引
|
|
|
CREATE INDEX idx_booking_slot_start_end_time ON booking_slot(start_time, end_time);
|
|
|
|
|
|
-- 在account表的status字段上创建索引
|
|
|
CREATE INDEX idx_account_status ON account(status);
|
|
|
|
|
|
-- 在student表的name字段上创建索引
|
|
|
CREATE INDEX idx_student_name ON student(name);
|
|
|
|
|
|
-- 在coach表的name字段上创建索引
|
|
|
CREATE INDEX idx_coach_name ON coach(name);
|
|
|
|
|
|
-- 在venue表的name字段上创建索引
|
|
|
CREATE INDEX idx_venue_name ON venue(name);
|
|
|
|
|
|
-- 在personal_info表的email字段上创建唯一索引
|
|
|
CREATE UNIQUE INDEX idx_personal_info_email ON personal_info(email);
|
|
|
-- 添加新学员的存储过程
|
|
|
CREATE OR REPLACE FUNCTION AddNewStudent(
|
|
|
_student_id VARCHAR(255),
|
|
|
_name VARCHAR(255),
|
|
|
_password VARCHAR(255),
|
|
|
_phone VARCHAR(20),
|
|
|
_email VARCHAR(255)
|
|
|
) RETURNS VOID AS $$
|
|
|
DECLARE
|
|
|
_info_id INT;
|
|
|
BEGIN
|
|
|
INSERT INTO personal_info (phone, email) VALUES (_phone, _email) RETURNING info_id INTO _info_id;
|
|
|
INSERT INTO student (student_id, name, password, personal_info_id) VALUES (_student_id, _name, _password, _info_id);
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
-- 更新教练电话的存储过程
|
|
|
CREATE OR REPLACE FUNCTION UpdateCoachPhone(
|
|
|
_coach_id VARCHAR(255),
|
|
|
_new_phone VARCHAR(20)
|
|
|
) RETURNS VOID AS $$
|
|
|
BEGIN
|
|
|
UPDATE personal_info SET phone = _new_phone WHERE info_id = (SELECT personal_info_id FROM coach WHERE coach_id = _coach_id);
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
-- 获取某个学生的所有预约的存储过程
|
|
|
CREATE OR REPLACE FUNCTION GetStudentAppointments(
|
|
|
_student_id VARCHAR(255)
|
|
|
) RETURNS TABLE(appointment_id INT, coach_name VARCHAR(255), venue_name VARCHAR(255), period VARCHAR(50), status VARCHAR(50)) AS $$
|
|
|
BEGIN
|
|
|
RETURN QUERY
|
|
|
SELECT a.appointment_id, c.name AS coach_name, v.name AS venue_name, a.period, a.status
|
|
|
FROM appointment a
|
|
|
JOIN coach c ON a.coach_id = c.coach_id
|
|
|
JOIN venue v ON a.venue_id = v.venue_id
|
|
|
WHERE a.student_id = _student_id;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
-- 获取某个教练的所有反馈的存储过程
|
|
|
CREATE OR REPLACE FUNCTION GetCoachFeedbacks(
|
|
|
_coach_id VARCHAR(255)
|
|
|
) RETURNS TABLE(feedback_id INT, content TEXT, date DATE) AS $$
|
|
|
BEGIN
|
|
|
RETURN QUERY
|
|
|
SELECT f.feedback_id, f.content, f.date
|
|
|
FROM feedback f
|
|
|
JOIN training_record t ON f.feedback_id = t.feedback_id
|
|
|
WHERE t.coach_id = _coach_id;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
-- 获取某个场地的所有可用时间段的存储过程
|
|
|
CREATE OR REPLACE FUNCTION GetAvailableSlotsForVenue(
|
|
|
_venue_id INT
|
|
|
) RETURNS TABLE(slot_id INT, start_time TIME, end_time TIME) AS $$
|
|
|
BEGIN
|
|
|
RETURN QUERY
|
|
|
SELECT bs.slot_id, bs.start_time, bs.end_time
|
|
|
FROM booking_slot bs
|
|
|
LEFT JOIN appointment a ON bs.venue_id = a.venue_id AND bs.start_time <= a.period::time AND bs.end_time >= a.period::time
|
|
|
WHERE bs.venue_id = _venue_id AND a.appointment_id IS NULL;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
-- 当插入新的学生时,自动在personal_info表中插入一条记录
|
|
|
CREATE OR REPLACE FUNCTION InsertPersonalInfoOnStudentInsert()
|
|
|
RETURNS TRIGGER AS $$
|
|
|
BEGIN
|
|
|
INSERT INTO personal_info (phone, email) VALUES ('', '');
|
|
|
NEW.personal_info_id := currval('personal_info_info_id_seq');
|
|
|
RETURN NEW;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE TRIGGER trigger_insert_personal_info_on_student_insert
|
|
|
BEFORE INSERT ON student
|
|
|
FOR EACH ROW EXECUTE FUNCTION InsertPersonalInfoOnStudentInsert();
|
|
|
|
|
|
-- 当删除一个教练时,自动删除其关联的personal_info记录
|
|
|
CREATE OR REPLACE FUNCTION DeletePersonalInfoOnCoachDelete()
|
|
|
RETURNS TRIGGER AS $$
|
|
|
BEGIN
|
|
|
DELETE FROM personal_info WHERE info_id = OLD.personal_info_id;
|
|
|
RETURN OLD;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE TRIGGER trigger_delete_personal_info_on_coach_delete
|
|
|
AFTER DELETE ON coach
|
|
|
FOR EACH ROW EXECUTE FUNCTION DeletePersonalInfoOnCoachDelete();
|
|
|
|
|
|
-- 当插入新的预约时,检查是否已经有重叠的时间段
|
|
|
CREATE OR REPLACE FUNCTION CheckOverlapBeforeAppointmentInsert()
|
|
|
RETURNS TRIGGER AS $$
|
|
|
DECLARE
|
|
|
overlap_count INT;
|
|
|
BEGIN
|
|
|
SELECT COUNT(*) INTO overlap_count
|
|
|
FROM appointment a
|
|
|
WHERE a.venue_id = NEW.venue_id
|
|
|
AND ((a.period = NEW.period)
|
|
|
OR (a.period < NEW.period AND a.period + INTERVAL '1 hour' > NEW.period)
|
|
|
OR (a.period > NEW.period AND a.period < NEW.period + INTERVAL '1 hour'));
|
|
|
IF overlap_count > 0 THEN
|
|
|
RAISE EXCEPTION 'There is an overlapping appointment at the same time and place.';
|
|
|
END IF;
|
|
|
RETURN NEW;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE TRIGGER trigger_check_overlap_before_appointment_insert
|
|
|
BEFORE INSERT ON appointment
|
|
|
FOR EACH ROW EXECUTE FUNCTION CheckOverlapBeforeAppointmentInsert();
|
|
|
|
|
|
-- 当更新发票金额时,确保金额不小于零
|
|
|
CREATE OR REPLACE FUNCTION EnsureInvoiceAmountNonNegative()
|
|
|
RETURNS TRIGGER AS $$
|
|
|
BEGIN
|
|
|
IF NEW.amount < 0 THEN
|
|
|
RAISE EXCEPTION 'Invoice amount cannot be negative.';
|
|
|
END IF;
|
|
|
RETURN NEW;
|
|
|
END;
|
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
|
|
CREATE TRIGGER trigger_ensure_invoice_amount_non_negative
|
|
|
BEFORE UPDATE ON invoice
|
|
|
FOR EACH ROW EXECUTE FUNCTION EnsureInvoiceAmountNonNegative();
|
|
|
-- 创建角色
|
|
|
CREATE ROLE AdminRole LOGIN PASSWORD 'admin_password';
|
|
|
CREATE ROLE CoachRole LOGIN PASSWORD 'coach_password';
|
|
|
CREATE ROLE StudentRole LOGIN PASSWORD 'student_password';
|
|
|
|
|
|
-- 授予AdminRole权限
|
|
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE account_account_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE venue_venue_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE student_student_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE coach_coach_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE appointment_appointment_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE feedback_feedback_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE invoice_invoice_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE personal_info_info_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE training_record_record_id_seq TO AdminRole;
|
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCE booking_slot_slot_id_seq TO AdminRole;
|
|
|
|
|
|
-- 授予CoachRole权限
|
|
|
GRANT SELECT, INSERT, UPDATE ON appointment TO CoachRole;
|
|
|
GRANT SELECT, INSERT, UPDATE ON feedback TO CoachRole;
|
|
|
GRANT SELECT, INSERT, UPDATE ON training_record TO CoachRole;
|
|
|
|
|
|
-- 授予StudentRole权限
|
|
|
GRANT SELECT, INSERT, UPDATE ON appointment TO StudentRole;
|
|
|
GRANT SELECT, INSERT, UPDATE ON feedback TO StudentRole;
|
|
|
GRANT SELECT, INSERT, UPDATE ON training_record TO StudentRole;
|
|
|
GRANT SELECT ON invoice TO StudentRole; |