Relational Design
This page documents the relational design for BEST Skopje Hub, based on ERModel_v12. The design uses partial transformation from the ER model into a PostgreSQL relational schema. Strong entities are mapped directly to relations, weak entities include the primary keys of their owner entities, specializations are mapped with one relation for the supertype and one relation for each subtype, and M:N relationships are mapped as separate relations.
Descriptive representation of the relational schema
students(
student_index, first_name, last_name, faculty, field_of_studies,
year_of_studies, birthday, email, phone_number,
PK(student_index)
)
members(
student_index,
PK(student_index),
FK(student_index) -> students(student_index)
)
observers(
student_index,
PK(student_index),
FK(student_index) -> members(student_index)
)
young_members(
student_index, mentor_index,
PK(student_index),
FK(student_index) -> members(student_index),
FK(mentor_index) -> full_members(student_index)
)
full_members(
student_index,
PK(student_index),
FK(student_index) -> members(student_index)
)
alumni(
student_index,
PK(student_index),
FK(student_index) -> members(student_index)
)
membership_stages(
ms_id, ms_name,
PK(ms_id),
UNIQUE(ms_name)
)
membership_stage_records(
msr_id, student_index, ms_id, valid_from, valid_to,
PK(msr_id),
FK(student_index) -> members(student_index),
FK(ms_id) -> membership_stages(ms_id)
)
organizational_functions(
of_id, of_title,
PK(of_id),
UNIQUE(of_title)
)
board(
of_id,
PK(of_id),
FK(of_id) -> organizational_functions(of_id)
)
non_board(
of_id,
PK(of_id),
FK(of_id) -> organizational_functions(of_id)
)
function_mandates(
fm_id, student_index, of_id, mandate_year,
PK(fm_id),
FK(student_index) -> full_members(student_index),
FK(of_id) -> organizational_functions(of_id)
)
activity_types(
at_id, at_name, at_description,
PK(at_id),
UNIQUE(at_name)
)
locations(
l_id, l_name,
PK(l_id),
UNIQUE(l_name)
)
activity_sessions(
as_id, at_id, l_id, as_start_time,
PK(as_id),
FK(at_id) -> activity_types(at_id),
FK(l_id) -> locations(l_id)
)
related_to(
as_id, ee_id,
PK(as_id),
FK(as_id) -> activity_sessions(as_id),
FK(ee_id) -> event_editions(ee_id)
)
event_types(
et_id, et_name, et_description,
PK(et_id),
UNIQUE(et_name)
)
event_editions(
ee_id, et_id, ee_title, ee_start_time, ee_end_time,
PK(ee_id),
FK(et_id) -> event_types(et_id)
)
event_edition_locations(
ee_id, l_id,
PK(ee_id, l_id),
FK(ee_id) -> event_editions(ee_id),
FK(l_id) -> locations(l_id)
)
event_session_types(
est_id, est_name, est_description,
PK(est_id),
UNIQUE(est_name)
)
event_function_types(
eft_id, eft_name,
PK(eft_id),
UNIQUE(eft_name)
)
event_sessions(
ee_id, es_no, est_id, l_id, es_start_time, es_title, needed_no_volunteers,
PK(ee_id, es_no),
FK(ee_id) -> event_editions(ee_id),
FK(est_id) -> event_session_types(est_id),
FK(l_id) -> locations(l_id)
)
event_functions(
ee_id, ef_no, eft_id,
PK(ee_id, ef_no),
FK(ee_id) -> event_editions(ee_id),
FK(eft_id) -> event_function_types(eft_id)
)
applications(
application_id, student_index, ee_id, ef_no,
motivational_letter, application_type, application_status,
PK(application_id),
FK(student_index) -> students(student_index),
FK(ee_id) -> event_editions(ee_id),
FK(ee_id, ef_no) -> event_functions(ee_id, ef_no)
)
holds_event_function(
student_index, ee_id, ef_no,
PK(student_index, ee_id, ef_no),
FK(student_index) -> members(student_index),
FK(ee_id, ef_no) -> event_functions(ee_id, ef_no)
)
participation(
student_index, as_id, rsvp_status, rsvp_at, attendance_status,
PK(student_index, as_id),
FK(student_index) -> students(student_index),
FK(as_id) -> activity_sessions(as_id)
)
required_attendance(
student_index, as_id,
PK(student_index, as_id),
FK(student_index) -> members(student_index),
FK(as_id) -> activity_sessions(as_id)
)
announces_absence(
student_index, as_id, reason, announced_at,
PK(student_index, as_id),
FK(student_index) -> members(student_index),
FK(as_id) -> activity_sessions(as_id)
)
responsible_for(
student_index, as_id,
PK(student_index, as_id),
FK(student_index) -> members(student_index),
FK(as_id) -> activity_sessions(as_id)
)
volunteers(
student_index, ee_id, es_no, volunteer_role,
PK(student_index, ee_id, es_no),
FK(student_index) -> members(student_index),
FK(ee_id, es_no) -> event_sessions(ee_id, es_no)
)
companies(
c_id, c_name, naframa_reference,
PK(c_id)
)
cooperation(
c_id, ee_id, cooperation_type,
PK(c_id, ee_id),
FK(c_id) -> companies(c_id),
FK(ee_id) -> event_editions(ee_id)
)
In the physical PostgreSQL schema, application_id is implemented as an automatically generated identity value. It is still listed as the primary key of applications, but it does not have to be manually inserted in the data loading script.
The applications relation is used only when there is an actual application process. Public attendance for events such as Job Fair, and open registration-style attendance for events such as beBESTie, is not represented as an application in this phase. For Hackathon and BEST Course in Summer participation applications, the motivational_letter attribute represents the most important motivation/answer kept from an external application form.
The specialization of students into members and then into observers, young members, full members, and alumni is represented through subtype tables whose primary keys are also foreign keys. young_members also contains mentor_index, which represents the mentorship relationship directly as a foreign key to full_members.
The weak entities from ERModel_v12 are represented with composite primary keys where they still depend on an owner entity. event_functions uses (ee_id, ef_no), and event_sessions uses (ee_id, es_no). Other concrete entities such as activity_sessions and event_editions use their own numeric identifiers.
The Locations entity is represented with locations. The relationship between EventEditions and Locations is represented with event_edition_locations.
The M:N relationships participation, required_attendance, announces_absence, responsible_for, volunteers, cooperation, holds_event_function, and the event-edition location relationship are represented as separate relations. The relationship related_to is represented separately because only some activity sessions are connected to event editions; Placing ee_id directly in activity_sessions would create nullable values for independent activities such as weekly meetings, so the absence of a related_to row represents that no event edition is connected. The relationships applies_with, for_function, for_participation, available_for, edition_of, part_of, session_inst_of, instance_of, takes_place_at, held_at, is_of_function_type, for_member, for_stage, held_by, and mandate_of are represented by foreign keys in the corresponding N-side relations. The restriction that an absence may be announced only when attendance is required is a business rule to be enforced later through application logic or a database trigger; it is not represented as an additional relationship in this phase.
DDL script for creating the database schema and objects
The DDL script recreates the official project schema and all database objects. It first drops the existing project schema if it exists, then creates the schema, tables, primary keys, foreign keys, and checks.
DML script for filling tables with data
The DML script recreates realistic sample data for all relations. It clears the current contents of the tables and inserts connected sample data for students, members, membership stages, activity sessions, event-related activity links, event editions, event sessions, event function types, event function slots, actual event function holders, applications, companies, volunteering, mentorship, attendance/RSVP, required attendance, announced absences, and organizational functions.
Relational diagram
The relational diagram was exported from DBeaver after running schema_creation.sql in the assigned PostgreSQL database and opening the project schema diagram. The diagram uses crow-feet notation.
AI Use
AI was used during this phase as a consultation, drafting, and checking tool while preparing the relational design from the already completed ER model. The AI-assisted parts were reviewed against the existing ER model and project requirements before being included.
Full AI usage documentation: RelationalDesignAIUsage.
Attachments (3)
- schema_creation.sql (10.2 KB ) - added by 5 days ago.
- relational_schema.png (136.2 KB ) - added by 5 days ago.
- data_load.sql (45.5 KB ) - added by 3 days ago.
Download all attachments as: .zip

