cloned from code.mipt.ru
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 100 KiB |
@@ -0,0 +1,77 @@
|
||||
CREATE OR REPLACE FUNCTION fn_low_enrollment(threshold INTEGER)
|
||||
RETURNS TABLE(
|
||||
discipline_id INTEGER,
|
||||
discipline_name VARCHAR,
|
||||
department_name VARCHAR,
|
||||
enrolled BIGINT
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT d.id,
|
||||
d.name AS discipline_name,
|
||||
dept.name AS department_name,
|
||||
COUNT(st.id) AS enrolled
|
||||
FROM Disciplines d
|
||||
LEFT JOIN Departments dept ON d.department_id = dept.id
|
||||
LEFT JOIN Statements st ON st.discipline_id = d.id
|
||||
GROUP BY d.id, d.name, dept.name
|
||||
HAVING COUNT(st.id) < threshold;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fn_generate_transcript(p_student INTEGER)
|
||||
RETURNS TEXT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
rec RECORD;
|
||||
student_info RECORD;
|
||||
transcript TEXT;
|
||||
BEGIN
|
||||
SELECT p.name || ' ' || p.surname AS full_name,
|
||||
s.group_id,
|
||||
s.status
|
||||
INTO student_info
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
WHERE s.id = p_student;
|
||||
|
||||
transcript := 'Transcript for student ' || p_student || E'\n'
|
||||
|| 'Name: ' || student_info.full_name || E'\n'
|
||||
|| 'Group: ' || student_info.group_id || E'\n'
|
||||
|| 'Status: ' || student_info.status || E'\n' || E'\n';
|
||||
|
||||
FOR rec IN
|
||||
SELECT d.name AS discipline_name,
|
||||
st.grade,
|
||||
to_char(st.conducted_at,'YYYY-MM-DD') AS date
|
||||
FROM Statements st
|
||||
JOIN Disciplines d ON st.discipline_id = d.id
|
||||
WHERE st.student_id = p_student
|
||||
ORDER BY st.conducted_at
|
||||
LOOP
|
||||
transcript := transcript || rec.date || ' - ' || rec.discipline_name || ': ' || rec.grade || E'\n';
|
||||
END LOOP;
|
||||
|
||||
RETURN transcript;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE sp_graduate_by_admission_year(p_year INTEGER)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
rec RECORD;
|
||||
BEGIN
|
||||
FOR rec IN
|
||||
SELECT s.id, g.study_starts
|
||||
FROM Students s
|
||||
JOIN Groups g ON s.group_id = g.group_id
|
||||
WHERE EXTRACT(YEAR FROM g.study_starts) = p_year
|
||||
AND s.status = 'УЧИТСЯ'
|
||||
LOOP
|
||||
INSERT INTO Movement(student_id, type, new_status, issued_at)
|
||||
VALUES (rec.id, 'ОТЧИСЛЕН', 'ОТЧИСЛЕН', NOW());
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE INDEX idx_physicals_fulltext_name ON Physicals
|
||||
USING GIN (to_tsvector('russian', name || ' ' || surname));
|
||||
|
||||
-- GIN (Generalized Inverted Index) хранит для каждой лексемы список указателей на строки,
|
||||
-- в которых она встречается. Это ускоряет полнотекстовый поиск, так как позволяет быстро
|
||||
-- находить все документы, содержащие нужные слова, без полного сканирования таблицы.
|
||||
|
||||
CREATE INDEX idx_movement_recent ON Movement(issued_at)
|
||||
WHERE issued_at >= '2024-09-01'::timestamp;
|
||||
@@ -0,0 +1,192 @@
|
||||
-- #
|
||||
-- # "contingent-movement" project;
|
||||
-- # author: gazakbayev.net
|
||||
-- # ver: 1.0
|
||||
-- #
|
||||
|
||||
-- 1 Студенты с одинаковыми фамилиями
|
||||
|
||||
WITH student_info AS (
|
||||
SELECT
|
||||
s.id as student_id,
|
||||
p.passport_no,
|
||||
p.surname,
|
||||
p.name,
|
||||
g.group_id,
|
||||
f.name as faculty_name
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
JOIN Groups g ON s.group_id = g.group_id
|
||||
JOIN Faculties f ON g.faculty_id = f.id
|
||||
)
|
||||
SELECT
|
||||
a.surname,
|
||||
a.name as student1_name,
|
||||
b.name as student2_name,
|
||||
a.group_id as group1,
|
||||
b.group_id as group2,
|
||||
a.faculty_name as faculty1,
|
||||
b.faculty_name as faculty2
|
||||
FROM student_info a
|
||||
JOIN student_info b ON a.surname = b.surname
|
||||
AND a.student_id < b.student_id
|
||||
ORDER BY a.surname, a.name;
|
||||
|
||||
-- 2 Средний балл студентов
|
||||
|
||||
SELECT
|
||||
s.id AS student_id,
|
||||
p.name,
|
||||
p.surname,
|
||||
ROUND(AVG(st.grade)::numeric, 2) AS average_grade
|
||||
FROM
|
||||
Students s
|
||||
JOIN
|
||||
Physicals p ON s.person = p.passport_no
|
||||
LEFT JOIN
|
||||
Statements st ON s.id = st.student_id
|
||||
GROUP BY
|
||||
s.id, p.name, p.surname
|
||||
ORDER BY
|
||||
average_grade DESC NULLS LAST;
|
||||
|
||||
-- 3 Топ 5 лучших
|
||||
|
||||
SELECT
|
||||
s.id,
|
||||
p.surname,
|
||||
p.name,
|
||||
ROUND(AVG(st.grade)::numeric, 2) AS avg_grade,
|
||||
DENSE_RANK() OVER (ORDER BY AVG(st.grade) DESC) AS rank
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
JOIN Statements st ON s.id = st.student_id
|
||||
GROUP BY s.id, p.surname, p.name
|
||||
ORDER BY avg_grade DESC
|
||||
LIMIT 5;
|
||||
|
||||
-- 4 Количества у кафедры дисциплин и студентов
|
||||
|
||||
SELECT
|
||||
d.name AS department,
|
||||
COUNT(DISTINCT disc.id) AS discipline_count,
|
||||
COUNT(DISTINCT s.id) AS student_count
|
||||
FROM Departments d
|
||||
LEFT JOIN Disciplines disc ON d.id = disc.department_id
|
||||
LEFT JOIN Groups g ON d.id = g.department_id
|
||||
LEFT JOIN Students s ON g.group_id = s.group_id
|
||||
GROUP BY d.id
|
||||
HAVING COUNT(DISTINCT s.id) > 0
|
||||
ORDER BY student_count DESC;
|
||||
|
||||
-- 5 ФИ обучающихся (статус Учится)
|
||||
|
||||
SELECT
|
||||
p.surname,
|
||||
p.name
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM Statements st
|
||||
WHERE st.student_id = s.id
|
||||
)
|
||||
AND s.status = 'УЧИТСЯ';
|
||||
|
||||
-- 6 Прогресс изменения среднего балла по студентам
|
||||
|
||||
SELECT
|
||||
s.id,
|
||||
p.surname,
|
||||
p.name,
|
||||
g.group_id,
|
||||
st.conducted_at,
|
||||
st.grade,
|
||||
AVG(st.grade) OVER (PARTITION BY s.id ORDER BY st.conducted_at
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_avg
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
JOIN Groups g ON s.group_id = g.group_id
|
||||
JOIN Statements st ON s.id = st.student_id
|
||||
ORDER BY s.id, st.conducted_at;
|
||||
|
||||
-- 7 Список группы с обучающимися, с указанием оценки успеваемости студента в группе "Ниже среднего" или "Выше среднего"
|
||||
|
||||
WITH group_stats AS (
|
||||
SELECT
|
||||
s.group_id,
|
||||
AVG(st.grade) AS group_avg
|
||||
FROM Students s
|
||||
JOIN Statements st ON s.id = st.student_id
|
||||
GROUP BY s.group_id
|
||||
)
|
||||
SELECT
|
||||
s.id,
|
||||
p.surname,
|
||||
p.name,
|
||||
g.group_id,
|
||||
ROUND(AVG(st.grade)::numeric, 2) AS student_avg,
|
||||
gs.group_avg,
|
||||
CASE
|
||||
WHEN AVG(st.grade) > gs.group_avg THEN 'Выше среднего'
|
||||
WHEN AVG(st.grade) < gs.group_avg THEN 'Ниже среднего'
|
||||
ELSE 'Средний'
|
||||
END AS comparison
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
JOIN Groups g ON s.group_id = g.group_id
|
||||
JOIN Statements st ON s.id = st.student_id
|
||||
JOIN group_stats gs ON g.group_id = gs.group_id
|
||||
GROUP BY s.id, p.surname, p.name, g.group_id, gs.group_avg
|
||||
ORDER BY g.group_id, comparison;
|
||||
|
||||
-- 8 Статистика отчислений по месяцам, основываясь на движении контингента (приказах)
|
||||
SELECT
|
||||
DATE_TRUNC('month', m.issued_at) AS month,
|
||||
COUNT(*) AS dropouts,
|
||||
SUM(COUNT(*)) OVER (ORDER BY DATE_TRUNC('month', m.issued_at)) AS cumulative_total
|
||||
FROM Movement m
|
||||
WHERE m.type = 'ОТЧИСЛЕН'
|
||||
GROUP BY DATE_TRUNC('month', m.issued_at)
|
||||
ORDER BY month;
|
||||
|
||||
-- 9 Научные руководители студентов
|
||||
|
||||
SELECT
|
||||
s.id AS student_id,
|
||||
p.name AS student_name,
|
||||
p.surname AS student_surname,
|
||||
s.supervisor AS supervisor_id,
|
||||
ph.name AS supervisor_name,
|
||||
ph.surname AS supervisor_surname
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
LEFT JOIN Supervisors sup ON s.supervisor = sup.person
|
||||
LEFT JOIN Physicals ph ON sup.person = ph.passport_no
|
||||
WHERE s.supervisor IS NOT NULL
|
||||
ORDER BY student_surname;
|
||||
|
||||
-- 10 Вывод преподавателей из ведомости по ФИО, принимающим предметам, средней оценки и уровня халявности:
|
||||
|
||||
SELECT
|
||||
p.surname AS "Фамилия",
|
||||
p.name AS "Имя",
|
||||
STRING_AGG(DISTINCT d.name, ', ' ORDER BY d.name) AS "Принимает дисциплины",
|
||||
ROUND(AVG(st.grade), 2) AS "Средняя оценка",
|
||||
CASE
|
||||
WHEN AVG(st.grade) < 3 THEN 'Не халявный'
|
||||
WHEN AVG(st.grade) < 5 THEN 'Хороший'
|
||||
WHEN AVG(st.grade) < 8 THEN 'Халявный'
|
||||
ELSE 'Ультра халявный'
|
||||
END AS "Уровень халявности"
|
||||
FROM
|
||||
Statements st
|
||||
JOIN
|
||||
Physicals p ON st.examiner = p.passport_no
|
||||
JOIN
|
||||
Disciplines d ON st.discipline_id = d.id
|
||||
GROUP BY
|
||||
p.passport_no, p.surname, p.name
|
||||
HAVING
|
||||
COUNT(*) >= 5
|
||||
ORDER BY
|
||||
"Средняя оценка" DESC;
|
||||
@@ -0,0 +1,126 @@
|
||||
-- #
|
||||
-- # "contingent-movement" project;
|
||||
-- # author: gazakbayev.net
|
||||
-- # ver: 1.0
|
||||
-- #
|
||||
|
||||
CREATE TABLE Physicals (
|
||||
passport_no VARCHAR(30) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
surname VARCHAR(255) NOT NULL,
|
||||
birthday TIMESTAMP NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
mail VARCHAR(255) NOT NULL,
|
||||
citizenship VARCHAR(50) NOT NULL,
|
||||
address VARCHAR(255) NOT NULL,
|
||||
access_card VARCHAR(20) NOT NULL CHECK (access_card ~ '^[A-F0-9]{2}(:[A-F0-9]{2}){5}$'),
|
||||
access_level VARCHAR(10) NOT NULL CHECK (access_level IN ('КАМПУС', 'ОБЩЕЖИТИЯ', 'ПОЛНЫЙ'))
|
||||
);
|
||||
|
||||
CREATE TABLE Supervisors (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person VARCHAR(32) REFERENCES Physicals(passport_no),
|
||||
experience INTEGER NOT NULL CHECK (experience >= 0),
|
||||
defended_ratio REAL NOT NULL CHECK (defended_ratio >= 0 AND defended_ratio <= 1),
|
||||
qualification VARCHAR(30) NOT NULL CHECK (qualification IN ('МЛАДШИЙ НАУЧНЫЙ СОТРУДНИК', 'СТАРШИЙ НАУЧНЫЙ СОТРУДНИК', 'ВЕДУЩИЙ НАУЧНЫЙ СОТРУДНИК', 'ГЛАВНЫЙ НАУЧНЫЙ СОТРУДНИК', 'НАУЧНЫЙ СОТРУДНИК')),
|
||||
valid_from TIMESTAMP NOT NULL,
|
||||
valid_to TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE Faculties (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
acronym VARCHAR(10) NOT NULL,
|
||||
head VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
|
||||
vice VARCHAR(32) REFERENCES Physicals(passport_no),
|
||||
address VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE Departments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
acronym VARCHAR(10) NOT NULL,
|
||||
founded TIMESTAMP NOT NULL,
|
||||
head VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
|
||||
vice VARCHAR(32) REFERENCES Physicals(passport_no),
|
||||
secretary VARCHAR(32) REFERENCES Physicals(passport_no),
|
||||
faculty_id INTEGER REFERENCES Faculties(id)
|
||||
);
|
||||
|
||||
CREATE TABLE Programs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
specification VARCHAR(20) NOT NULL,
|
||||
degree VARCHAR(30) NOT NULL CHECK (degree IN ('BACHELOR', 'MAGISTER', 'ASPIRANT')),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
parent_id INTEGER REFERENCES Programs(id)
|
||||
);
|
||||
|
||||
CREATE TABLE Groups (
|
||||
group_id VARCHAR(10) PRIMARY KEY CHECK (group_id ~ '^[АБМ]\d{2}-\d{3}[а-я]?$'),
|
||||
faculty_id INTEGER NOT NULL REFERENCES Faculties(id),
|
||||
program_id INTEGER NOT NULL REFERENCES Programs(id),
|
||||
department_id INTEGER REFERENCES Departments(id),
|
||||
study_starts TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
study_ends TIMESTAMP NOT NULL DEFAULT NOW() + INTERVAL '4 years'
|
||||
);
|
||||
|
||||
CREATE TABLE Students (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person VARCHAR(32) NOT NULL UNIQUE REFERENCES Physicals(passport_no),
|
||||
group_id VARCHAR(10) NOT NULL REFERENCES Groups(group_id),
|
||||
supervisor_id INTEGER REFERENCES Supervisors(id),
|
||||
education_form VARCHAR(255) NOT NULL CHECK (education_form IN ('ОЧНАЯ', 'ЗАОЧНАЯ', 'ВЕЧЕРНЯЯ')),
|
||||
status VARCHAR(30) NOT NULL CHECK (status IN ('УЧИТСЯ', 'В АКАДЕМИЧЕСКОМ ОТПУСКЕ', 'ОТЧИСЛЕН'))
|
||||
);
|
||||
|
||||
CREATE TABLE Family (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
surname VARCHAR(255) NOT NULL,
|
||||
kinship VARCHAR(10) NOT NULL CHECK (kinship IN ('MOTHER', 'FATHER', 'BROTHER', 'SISTER', 'ANOTHER')),
|
||||
phone VARCHAR(20) NOT NULL UNIQUE,
|
||||
address VARCHAR(255) NOT NULL,
|
||||
CONSTRAINT unique_student_kinship UNIQUE (person, kinship)
|
||||
);
|
||||
|
||||
CREATE TABLE Files (
|
||||
id SERIAL PRIMARY KEY,
|
||||
student_id INTEGER NOT NULL REFERENCES Students(id),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description VARCHAR(255) NOT NULL,
|
||||
extension VARCHAR(5) NOT NULL CHECK (extension IN ('PNG', 'JPEG', 'PDF')),
|
||||
size NUMERIC(10,2) NOT NULL CHECK (size <= 20),
|
||||
path VARCHAR(255) NOT NULL UNIQUE,
|
||||
loaded_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE Disciplines (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
department_id INTEGER NOT NULL REFERENCES Departments(id),
|
||||
credit_units INTEGER NOT NULL,
|
||||
academic_hours INTEGER NOT NULL,
|
||||
general_hours INTEGER NOT NULL CHECK (general_hours > academic_hours),
|
||||
is_annual BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE Statements (
|
||||
id SERIAL PRIMARY KEY,
|
||||
student_id INTEGER NOT NULL REFERENCES Students(id),
|
||||
discipline_id INTEGER NOT NULL REFERENCES Disciplines(id),
|
||||
examiner VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
|
||||
try_no INTEGER NOT NULL CHECK (try_no BETWEEN 0 AND 2),
|
||||
grade INTEGER NOT NULL CHECK (grade BETWEEN 1 AND 10),
|
||||
conducted_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE Movement (
|
||||
id SERIAL PRIMARY KEY,
|
||||
student_id INTEGER NOT NULL REFERENCES Students(id),
|
||||
type VARCHAR(30) NOT NULL CHECK (type IN ('ЗАЧИСЛЕН', 'ВОССТАНОВЛЕН', 'ОТЧИСЛЕН', 'В АКАДЕМИЧЕСКИЙ ОТПУСК', 'ПЕРЕВОД В ДРУГУЮ ГРУППУ')),
|
||||
new_group VARCHAR(10) REFERENCES Groups(group_id),
|
||||
new_status VARCHAR(30) CHECK (new_status IN ('УЧИТСЯ', 'В АКАДЕМИЧЕСКОМ ОТПУСКЕ', 'ОТЧИСЛЕН')),
|
||||
issued_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
-- Trigger 1
|
||||
|
||||
CREATE OR REPLACE FUNCTION trg_prevent_group_delete()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
||||
DECLARE cnt INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO cnt FROM Students WHERE group_id = OLD.group_id AND status = 'УЧИТСЯ';
|
||||
IF cnt > 0 THEN
|
||||
RAISE EXCEPTION 'Cannot delete group %: % active students exist', OLD.group_id, cnt;
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER prevent_group_delete
|
||||
BEFORE DELETE ON Groups
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE trg_prevent_group_delete();
|
||||
|
||||
-- Trigger 2
|
||||
CREATE OR REPLACE FUNCTION trg_lowercase_mail()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
NEW.mail := lower(NEW.mail);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER lowercase_mail
|
||||
BEFORE INSERT OR UPDATE ON Physicals
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE trg_lowercase_mail();
|
||||
|
||||
-- Trigger 3
|
||||
CREATE OR REPLACE FUNCTION trg_sync_student_status()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
UPDATE Students
|
||||
SET status = NEW.new_status
|
||||
WHERE id = NEW.student_id;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER sync_student_status
|
||||
AFTER INSERT ON Movement
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE trg_sync_student_status();
|
||||
|
||||
-- Trigger SCD 2
|
||||
CREATE OR REPLACE FUNCTION trg_supervisors_history()
|
||||
RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.valid_from = NOW();
|
||||
INSERT INTO Supervisors (
|
||||
person,
|
||||
experience,
|
||||
defended_ratio,
|
||||
qualification,
|
||||
valid_from,
|
||||
valid_to
|
||||
) VALUES (
|
||||
OLD.person,
|
||||
OLD.experience,
|
||||
OLD.defended_ratio,
|
||||
OLD.qualification,
|
||||
OLD.valid_from,
|
||||
NOW()
|
||||
);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE TRIGGER trg_supervisors_scd
|
||||
BEFORE UPDATE ON Supervisors
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE trg_supervisors_history();
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_unique_supervisor()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM Supervisors
|
||||
WHERE person = NEW.person
|
||||
AND experience = NEW.experience
|
||||
AND defended_ratio = NEW.defended_ratio
|
||||
AND qualification = NEW.qualification
|
||||
AND valid_from = NEW.valid_from
|
||||
AND (valid_to = NEW.valid_to OR (valid_to IS NULL AND NEW.valid_to IS NULL))
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Дублирующая запись в таблице Supervisors. Все поля (кроме id) должны быть уникальными.';
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER unique_supervisor_trigger
|
||||
BEFORE INSERT ON Supervisors
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_unique_supervisor();
|
||||
@@ -0,0 +1,22 @@
|
||||
-- STUDENT PERFORMANCE --
|
||||
CREATE OR REPLACE VIEW vw_student_performance AS
|
||||
SELECT s.id AS student_id,
|
||||
p.name || ' ' || p.surname AS full_name,
|
||||
s.group_id,
|
||||
COUNT(st.id) FILTER (WHERE st.grade >= 3) AS passes,
|
||||
COUNT(st.id) FILTER (WHERE st.grade < 3) AS fails,
|
||||
ROUND(AVG(st.grade)::numeric, 2) AS avg_grade
|
||||
FROM Students s
|
||||
JOIN Physicals p ON s.person = p.passport_no
|
||||
LEFT JOIN Statements st ON s.id = st.student_id
|
||||
GROUP BY s.id, p.name, p.surname, s.group_id;
|
||||
|
||||
-- BIRTHDAYS ON THIS MONTH --
|
||||
CREATE VIEW vw_birthdays_month AS
|
||||
SELECT passport_no,
|
||||
name || ' ' || surname AS full_name,
|
||||
birthday,
|
||||
EXTRACT(DAY FROM birthday) AS day_of_month
|
||||
FROM Physicals
|
||||
WHERE EXTRACT(MONTH FROM birthday) = EXTRACT(MONTH FROM NOW())
|
||||
ORDER BY day_of_month;
|
||||
@@ -0,0 +1,110 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 YaBrowser/25.2.0.0 Safari/537.36" version="26.1.1">
|
||||
<diagram name="Концептуальная модель" id="uXQN3Tqoo_ou7SFrJxsO">
|
||||
<mxGraphModel dx="954" dy="603" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-10" target="M9otMMDKzzFARfVN8Q2O-22" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=circle;endFill=0;endSize=3;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-10" target="M9otMMDKzzFARfVN8Q2O-16" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;startArrow=ERone;startFill=0;endArrow=ERone;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-10" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-10" value="Students" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="200" y="440" width="120" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-23" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-11" target="M9otMMDKzzFARfVN8Q2O-10" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-11" value="Family" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="10" y="530" width="100" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=none;" parent="1" source="M9otMMDKzzFARfVN8Q2O-13" target="M9otMMDKzzFARfVN8Q2O-10" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-13" value="Files" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="10" y="590" width="100" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-41" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;startFill=0;startArrow=ERmany;" parent="1" source="M9otMMDKzzFARfVN8Q2O-14" target="M9otMMDKzzFARfVN8Q2O-10" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-14" value="Movement" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="90" y="230" width="100" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-15" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-42" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;endArrow=none;startFill=0;startArrow=ERmany;" parent="1" source="M9otMMDKzzFARfVN8Q2O-15" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="200" y="490" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="70" y="200" />
|
||||
<mxPoint x="70" y="490" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=ERone;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-15" target="M9otMMDKzzFARfVN8Q2O-17" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-15" value="Statements" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="90" y="180" width="100" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;endArrow=none;startFill=0;startArrow=ERone;" parent="1" source="M9otMMDKzzFARfVN8Q2O-16" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-16" value="Supervisors" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="200" y="360" width="120" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-17" target="M9otMMDKzzFARfVN8Q2O-19" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-17" value="Disciplines" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="550" y="150" width="120" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-18" value="Physicals" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="330" y="160" width="150" height="180" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=ERmany;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-19" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-19" value="Departments" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="640" y="290" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="xU2I2qLbYURmXa2A43uc-1" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=oval;startFill=0;endArrow=ERmany;endFill=0;" edge="1" parent="1" target="M9otMMDKzzFARfVN8Q2O-19">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="700" y="406" as="sourcePoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="700" y="380" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-20" value="Faculties" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="640" y="410" width="120" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;endArrow=ERone;endFill=0;endSize=6;startArrow=ERone;startFill=0;startSize=6;" parent="1" source="M9otMMDKzzFARfVN8Q2O-21" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="650" y="600.4000000000001" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-21" value="Programs" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="560" y="550" width="120" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-22" target="M9otMMDKzzFARfVN8Q2O-21" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-36" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=ERone;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-22" target="M9otMMDKzzFARfVN8Q2O-20" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=circle;endFill=0;endSize=3;" parent="1" source="M9otMMDKzzFARfVN8Q2O-22" target="M9otMMDKzzFARfVN8Q2O-19" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="M9otMMDKzzFARfVN8Q2O-22" value="Groups" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
|
||||
<mxGeometry x="390" y="420" width="150" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,193 @@
|
||||
Table Students {
|
||||
id integer [pk, increment, not null, unique, note: 'Номер студенческого билета']
|
||||
person_id integer [not null, unique, note: 'Номер физического лица']
|
||||
group varchar [not null, note: 'Номер учебной группы']
|
||||
supervisor integer [note: 'Номер научного руководителя']
|
||||
education_form varchar [not null, default: 'Очная', note: 'Форма обучения студента']
|
||||
status varchar [not null, default: 'Учится', note: 'Статус обучающегося']
|
||||
}
|
||||
|
||||
Table Persons {
|
||||
id integer [pk, increment, not null, unique]
|
||||
name varchar [not null]
|
||||
surname varchar [not null]
|
||||
birthday timestamp [not null]
|
||||
phone varchar [not null]
|
||||
mail varchar [default: null]
|
||||
passport_no varchar [not null]
|
||||
citizenship varchar [not null]
|
||||
address varchar [not null]
|
||||
access_card varchar [not null]
|
||||
access_level varchar [not null]
|
||||
}
|
||||
|
||||
Table Groups {
|
||||
group varchar [pk, not null, unique]
|
||||
faculty_id integer [not null]
|
||||
program_id integer [not null]
|
||||
department_id integer [not null]
|
||||
study_starts timestamp [not null]
|
||||
study_ends timestamp [not null]
|
||||
}
|
||||
|
||||
Table Family {
|
||||
id integer [pk, increment, not null, unique]
|
||||
student_id integer [not null]
|
||||
name varchar [not null]
|
||||
surname varchar [not null]
|
||||
kinship varchar [not null, note: 'Степень родства: mthr, fthr, brth, sstr']
|
||||
phone varchar [not null]
|
||||
address varchar [not null]
|
||||
}
|
||||
|
||||
Table Files {
|
||||
id integer [pk, increment, not null, unique]
|
||||
student_id integer [not null]
|
||||
name varchar [not null]
|
||||
description varchar [not null]
|
||||
extension varchar [not null, note: 'available: png, jpeg, pdf']
|
||||
size numeric [not null, note: 'size in mb']
|
||||
path varchar [not null, note: 'path to file /var/www/student_files/<file_id>.<ext>']
|
||||
loaded_at timestamp [not null]
|
||||
}
|
||||
|
||||
Table Supervisors {
|
||||
id integer [pk, increment, not null, unique]
|
||||
person_id integer [not null]
|
||||
experience integer [not null]
|
||||
defended_ratio real [not null]
|
||||
qualification varchar [not null]
|
||||
}
|
||||
|
||||
Table Movement {
|
||||
id integer [pk, increment, not null, unique]
|
||||
student_id integer [not null]
|
||||
type varchar [not null]
|
||||
new_group varchar
|
||||
new_status varchar
|
||||
issued_at timestamp [not null]
|
||||
}
|
||||
|
||||
Table Departments {
|
||||
id integer [pk, increment, not null, unique]
|
||||
name varchar [not null]
|
||||
acronym varchar [not null]
|
||||
founded timestamp [not null]
|
||||
head integer [not null]
|
||||
vice integer
|
||||
secretary integer
|
||||
faculty_id integer
|
||||
}
|
||||
|
||||
Table Programs {
|
||||
id integer [pk, increment, not null, unique]
|
||||
specification varchar [not null, note: 'Key from Russian register']
|
||||
degree varchar [not null]
|
||||
name varchar [not null]
|
||||
parent_id integer [not null]
|
||||
}
|
||||
|
||||
Table Faculties {
|
||||
id integer [pk, increment, not null, unique]
|
||||
name varchar [not null]
|
||||
acronym varchar [not null]
|
||||
head integer [not null]
|
||||
vice integer
|
||||
address varchar [not null]
|
||||
}
|
||||
|
||||
Table Statements {
|
||||
id integer [pk, increment, not null, unique]
|
||||
student_id integer [not null]
|
||||
discipline_id integer [not null]
|
||||
examiner_id integer [not null]
|
||||
try_no integer [not null]
|
||||
grade integer [not null]
|
||||
conducted_at timestamp [not null]
|
||||
}
|
||||
|
||||
Table Disciplines {
|
||||
id integer [pk, increment, not null, unique]
|
||||
name varchar [not null]
|
||||
department_id integer [not null]
|
||||
credit_units integer [not null]
|
||||
academic_hours integer [not null]
|
||||
general_hours integer [not null]
|
||||
is_annual boolean [not null]
|
||||
}
|
||||
|
||||
Ref "Студент к физлицу" {
|
||||
Persons.id - Students.person_id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Загружен для" {
|
||||
Files.student_id > Students.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Руководитель к физлицу" {
|
||||
Departments.head - Persons.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Заместитель к физлицу" {
|
||||
Departments.vice - Persons.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Секретарь к физлицу" {
|
||||
Departments.secretary - Persons.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "К научному руководителю" {
|
||||
Students.supervisor > Supervisors.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "В учебной группе" {
|
||||
Students.group > Groups.group [delete: no action]
|
||||
}
|
||||
|
||||
Ref "План/программа группы" {
|
||||
Groups.program_id > Programs.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Группа факультета" {
|
||||
Groups.faculty_id > Faculties.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Имеет базовую кафедру" {
|
||||
Groups.department_id > Departments.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Ведомость по студенту" {
|
||||
Statements.student_id > Students.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Ведомость по дисциплине" {
|
||||
Statements.discipline_id > Disciplines.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Экзаменатор к физлицу" {
|
||||
Statements.examiner_id > Persons.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Дисциплина к кафедре" {
|
||||
Disciplines.department_id > Departments.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Подразделение факультета" {
|
||||
Departments.faculty_id > Faculties.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Приказ по студенту" {
|
||||
Movement.student_id > Students.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Научный руководитель к физлицу" {
|
||||
Supervisors.person_id - Persons.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Дочерний к программе" {
|
||||
Programs.parent_id - Programs.id [delete: no action]
|
||||
}
|
||||
|
||||
Ref "Родственник студента" {
|
||||
Family.student_id > Students.id [delete: no action]
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.9 MiB |