| 149 | | == Интегритетни ограничувања (UNIQUE, CHECK) == |
| 150 | | За да спречиме аномалии и дупликати, додадовме: |
| 151 | | |
| 152 | | {{{ |
| 153 | | #!sql |
| 154 | | -- UNIQUE: деловни правила |
| 155 | | ALTER TABLE "User" ADD CONSTRAINT uq_user_email UNIQUE (email); |
| 156 | | ALTER TABLE "User" ADD CONSTRAINT uq_user_username UNIQUE (username); |
| 157 | | ALTER TABLE "Instructor" ADD CONSTRAINT uq_instr_email UNIQUE (instructor_email); |
| 158 | | ALTER TABLE "Training" ADD CONSTRAINT uq_training_name UNIQUE (training_name); |
| 159 | | ALTER TABLE "Package" ADD CONSTRAINT uq_package_name UNIQUE (package_name); |
| 160 | | ALTER TABLE "Merch_Items" ADD CONSTRAINT uq_merch_item UNIQUE (item_name); |
| 161 | | |
| 162 | | -- "Slot" уникатност: спречува дупликат термини/локации |
| 163 | | ALTER TABLE "Class" |
| 164 | | ADD CONSTRAINT uq_class_slot UNIQUE (date, start_time, location, instructor_id); |
| 165 | | ALTER TABLE "Event" |
| 166 | | ADD CONSTRAINT uq_event_slot UNIQUE (event_name, date, time, location); |
| 167 | | |
| 168 | | -- CHECK: едноставни бизнис правила |
| 169 | | ALTER TABLE "Class" |
| 170 | | ADD CONSTRAINT ck_class_time_order CHECK (end_time > start_time), |
| 171 | | ADD CONSTRAINT ck_class_capacity_nonneg CHECK (capacity IS NULL OR capacity >= 0), |
| 172 | | ADD CONSTRAINT ck_seats_not_overflow CHECK (seats_available IS NULL OR capacity IS NULL OR seats_available <= capacity), |
| 173 | | ADD CONSTRAINT ck_seats_nonneg CHECK (seats_available IS NULL OR seats_available >= 0); |
| 174 | | |
| 175 | | ALTER TABLE "Package" |
| 176 | | ADD CONSTRAINT ck_package_price_pos CHECK (price > 0), |
| 177 | | ADD CONSTRAINT ck_package_num_pos CHECK (num_classes > 0); |
| 178 | | |
| 179 | | ALTER TABLE "Merch_Items" |
| 180 | | ADD CONSTRAINT ck_merch_price_pos CHECK (price > 0); |
| 181 | | }}} |
| 182 | | |
| 183 | | Забелешка: при обид за дупликат UNIQUE, PostgreSQL враќа грешка 23505 (unique violation) – тоа во апликацијата го прикажуваме како “Email already taken”, итн. |
| 184 | | |
| 185 | | == Индекси за перформанси == |
| 186 | | |
| 187 | | Индекси на мостови (по „другата“ страна од PK, за побрзи филтри): |
| 188 | | {{{ |
| 189 | | #!sql |
| 190 | | CREATE INDEX IF NOT EXISTS idx_ubc_class ON "User_Booked_Class"(class_id); |
| 191 | | CREATE INDEX IF NOT EXISTS idx_cht_training ON "Class_Includes_Training"(training_id); |
| 192 | | CREATE INDEX IF NOT EXISTS idx_ue_event ON "User_Event"(event_id); |
| 193 | | CREATE INDEX IF NOT EXISTS idx_upp_package ON "User_Purchased_Package"(package_id); |
| 194 | | CREATE INDEX IF NOT EXISTS idx_upm_merch ON "User_Purchased_Merch"(merch_id); |
| 195 | | CREATE INDEX IF NOT EXISTS idx_pic_class ON "Package_Includes_Class"(class_id); |
| 196 | | }}} |
| 197 | | |
| 198 | | Индекси за честите листања: |
| 199 | | {{{ |
| 200 | | #!sql |
| 201 | | CREATE INDEX IF NOT EXISTS idx_event_date_time ON "Event"(date, time); |
| 202 | | CREATE INDEX IF NOT EXISTS idx_class_date_time ON "Class"(date, start_time); |
| 203 | | }}} |