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 | | }}} |