| | 1 | = Database Creation = |
| | 2 | |
| | 3 | == Database DDL Script == |
| | 4 | Во овој дел е прикачена комплетната SQL скрипта за креирање на табелите на PostgreSQL DBMS. Скрипта содржи дефиниции за примарни и надворешни клучеви, автоматски генератори на клучни вредности (SERIAL) и соодветни ограничувања за домените. |
| | 5 | |
| | 6 | Оригиналната SQL скрипта е достапна тука: [attachment:VidiDB_Creation.sql VidiDB_Creation.sql] |
| | 7 | |
| | 8 | --- |
| | 9 | |
| | 10 | == DDL Structure by Logical Segments == |
| | 11 | |
| | 12 | === 1. User & Subscription Modules === |
| | 13 | Овие табели го менаџираат автентицирањето на корисниците, нивните уреди и активните претплати. Користиме комплексен релационен модел за да спречиме злоупотреба на профилите. |
| | 14 | |
| | 15 | {{{ |
| | 16 | #!sql |
| | 17 | CREATE TABLE Subscription ( |
| | 18 | SubscriptionID SERIAL PRIMARY KEY, |
| | 19 | Name varchar(255) NOT NULL, |
| | 20 | Price numeric(19, 2) NOT NULL CHECK (Price >= 0), |
| | 21 | MaxDevices int4 NOT NULL CHECK (MaxDevices > 0), |
| | 22 | VideoQuality varchar(255) |
| | 23 | ); |
| | 24 | |
| | 25 | CREATE TABLE "User" ( |
| | 26 | UserID SERIAL PRIMARY KEY, |
| | 27 | FirstName varchar(255), |
| | 28 | LastName varchar(255), |
| | 29 | Username varchar(255) NOT NULL UNIQUE, |
| | 30 | Email varchar(255) NOT NULL UNIQUE |
| | 31 | CHECK (Email ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'), |
| | 32 | password varchar(255) NOT NULL, |
| | 33 | Date_registered date DEFAULT CURRENT_DATE |
| | 34 | ); |
| | 35 | |
| | 36 | CREATE TABLE User_Subscription ( |
| | 37 | UserSubscriptionID SERIAL PRIMARY KEY, |
| | 38 | UserUserID int4 NOT NULL REFERENCES "User" (UserID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 39 | SubscriptionSubscriptionID int4 NOT NULL REFERENCES Subscription (SubscriptionID) ON DELETE RESTRICT ON UPDATE CASCADE, |
| | 40 | Start_date date NOT NULL DEFAULT CURRENT_DATE, |
| | 41 | End_date date CHECK (End_date IS NULL OR End_date >= Start_date), |
| | 42 | Status varchar(255), |
| | 43 | Auto_renew int4 DEFAULT 0 CHECK (Auto_renew IN (0, 1)) |
| | 44 | ); |
| | 45 | |
| | 46 | CREATE TABLE Devices ( |
| | 47 | DeviceID SERIAL PRIMARY KEY, |
| | 48 | DeviceType varchar(255), |
| | 49 | LastLogIn date, |
| | 50 | UserSubscriptionID int4 NOT NULL REFERENCES User_Subscription (UserSubscriptionID) ON DELETE RESTRICT ON UPDATE CASCADE |
| | 51 | ); |
| | 52 | |
| | 53 | CREATE TABLE User_Devices ( |
| | 54 | UserUserID int4 NOT NULL REFERENCES "User" (UserID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 55 | DevicesDeviceID int4 NOT NULL REFERENCES Devices (DeviceID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 56 | PRIMARY KEY (UserUserID, DevicesDeviceID) |
| | 57 | ); |
| | 58 | }}} |
| | 59 | |
| | 60 | === 2. Media Hierarchy & Watchables === |
| | 61 | Овој дел ја имплементира наследната структура каде филмовите (`Movie`) и сериите (`Series`) наследуваат од генералниот медиумски ентитет (`Media`). За ефикасно следење на репродукцијата, воведен е концептот на `Watchable` објекти. |
| | 62 | |
| | 63 | {{{ |
| | 64 | #!sql |
| | 65 | CREATE TABLE Media ( |
| | 66 | ContentID SERIAL PRIMARY KEY, |
| | 67 | title varchar(255) NOT NULL, |
| | 68 | description varchar(255), |
| | 69 | releaseDate date, |
| | 70 | AgeRating numeric(19, 0) CHECK (AgeRating >= 0 AND AgeRating <= 21) |
| | 71 | ); |
| | 72 | |
| | 73 | CREATE TABLE Movie ( |
| | 74 | MovieID int4 PRIMARY KEY REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 75 | Duration int4 CHECK (Duration > 0) |
| | 76 | ); |
| | 77 | |
| | 78 | CREATE TABLE Series ( |
| | 79 | SeriesID int4 PRIMARY KEY REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 80 | TotalSeasons int4 CHECK (TotalSeasons > 0) |
| | 81 | ); |
| | 82 | |
| | 83 | CREATE TABLE Season ( |
| | 84 | SeasonID SERIAL PRIMARY KEY, |
| | 85 | SeasonNumber int4 NOT NULL CHECK (SeasonNumber > 0), |
| | 86 | ReleaseYear int4, |
| | 87 | SeriesSeriesID int4 NOT NULL REFERENCES Series (SeriesID) ON DELETE CASCADE ON UPDATE CASCADE |
| | 88 | ); |
| | 89 | |
| | 90 | CREATE TABLE Episode ( |
| | 91 | EpisodeID SERIAL PRIMARY KEY, |
| | 92 | Title varchar(255), |
| | 93 | episodeNumber int4 NOT NULL CHECK (episodeNumber > 0), |
| | 94 | Duration int4 CHECK (Duration > 0), |
| | 95 | SeasonSeasonID int4 NOT NULL REFERENCES Season (SeasonID) ON DELETE CASCADE ON UPDATE CASCADE |
| | 96 | ); |
| | 97 | |
| | 98 | CREATE TABLE Watchable ( |
| | 99 | WatchableID SERIAL PRIMARY KEY, |
| | 100 | duration int4 CHECK (duration > 0), |
| | 101 | MovieID int4 REFERENCES Movie (MovieID) ON DELETE SET NULL ON UPDATE CASCADE, |
| | 102 | EpisodeID int4 REFERENCES Episode (EpisodeID) ON DELETE SET NULL ON UPDATE CASCADE |
| | 103 | ); |
| | 104 | }}} |
| | 105 | |
| | 106 | === 3. Metadata & Taxonomies === |
| | 107 | Овие табели служат за класификација на медиумската содржина по јазик, жанр и инволвирани артисти (актери, режисери итн.). Врските се реализирани преку чисти релациони табели од тип многу-кон-многу (N:M). |
| | 108 | |
| | 109 | {{{ |
| | 110 | #!sql |
| | 111 | CREATE TABLE Artist ( |
| | 112 | ArtistID SERIAL PRIMARY KEY, |
| | 113 | FirstName varchar(255), |
| | 114 | LastName varchar(255), |
| | 115 | BirthDate date, |
| | 116 | RoleType varchar(255) CHECK (RoleType IN ('Actor', 'Director', 'Cameraman', 'Both')) |
| | 117 | ); |
| | 118 | |
| | 119 | CREATE TABLE Genre ( |
| | 120 | GenreID SERIAL PRIMARY KEY, |
| | 121 | Name varchar(255) NOT NULL UNIQUE |
| | 122 | ); |
| | 123 | |
| | 124 | CREATE TABLE Language ( |
| | 125 | LanguageID SERIAL PRIMARY KEY, |
| | 126 | Name varchar(255) NOT NULL UNIQUE |
| | 127 | ); |
| | 128 | |
| | 129 | CREATE TABLE Content_Artist ( |
| | 130 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 131 | ArtistArtistID int4 NOT NULL REFERENCES Artist (ArtistID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 132 | PRIMARY KEY (ContentContentID, ArtistArtistID) |
| | 133 | ); |
| | 134 | |
| | 135 | CREATE TABLE Content_Genre ( |
| | 136 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 137 | GenreGenreID int4 NOT NULL REFERENCES Genre (GenreID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 138 | PRIMARY KEY (ContentContentID, GenreGenreID) |
| | 139 | ); |
| | 140 | |
| | 141 | CREATE TABLE Content_Language ( |
| | 142 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 143 | LanguageLanguageID int4 NOT NULL REFERENCES Language (LanguageID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 144 | PRIMARY KEY (ContentContentID, LanguageLanguageID) |
| | 145 | ); |
| | 146 | }}} |
| | 147 | |
| | 148 | === 4. User Interaction & History === |
| | 149 | Овој сегмент ги следи сите кориснички акции во системот: оценување, пишување рецензии, додавање во листи за следење и детална историја на гледање со статус на прогрес. |
| | 150 | |
| | 151 | {{{ |
| | 152 | #!sql |
| | 153 | CREATE TABLE Rating ( |
| | 154 | RatingID SERIAL PRIMARY KEY, |
| | 155 | Rating_Date date DEFAULT CURRENT_DATE, |
| | 156 | RatingValue int4 NOT NULL CHECK (RatingValue BETWEEN 1 AND 10), |
| | 157 | UserUserID int4 NOT NULL REFERENCES "User" (UserID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 158 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 159 | UNIQUE (UserUserID, ContentContentID) |
| | 160 | ); |
| | 161 | |
| | 162 | CREATE TABLE Review ( |
| | 163 | ReviewID SERIAL PRIMARY KEY, |
| | 164 | Comment varchar(255), |
| | 165 | ReviewDate date DEFAULT CURRENT_DATE, |
| | 166 | UserUserID int4 NOT NULL REFERENCES "User" (UserID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 167 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE |
| | 168 | ); |
| | 169 | |
| | 170 | CREATE TABLE WatchHistory ( |
| | 171 | HistoryID SERIAL PRIMARY KEY, |
| | 172 | WatchedAt date DEFAULT CURRENT_DATE, |
| | 173 | Progress_percentage int4 CHECK (Progress_percentage BETWEEN 0 AND 100), |
| | 174 | UserUserID int4 NOT NULL REFERENCES "User" (UserID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 175 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 176 | WatchableWatchableID int4 NOT NULL REFERENCES Watchable (WatchableID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 177 | DevicesDeviceID int4 REFERENCES Devices (DeviceID) ON DELETE SET NULL ON UPDATE CASCADE |
| | 178 | ); |
| | 179 | |
| | 180 | CREATE TABLE Watchlist ( |
| | 181 | WatchlistID SERIAL PRIMARY KEY, |
| | 182 | dateAdded date DEFAULT CURRENT_DATE, |
| | 183 | UserUserID int4 NOT NULL REFERENCES "User" (UserID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 184 | ContentContentID int4 NOT NULL REFERENCES Media (ContentID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 185 | WatchableWatchableID int4 NOT NULL REFERENCES Watchable (WatchableID) ON DELETE CASCADE ON UPDATE CASCADE, |
| | 186 | UNIQUE (UserUserID, ContentContentID) |
| | 187 | ); |
| | 188 | }}} |
| | 189 | |
| | 190 | --- |
| | 191 | |
| | 192 | == Key Implementation Highlights == |
| | 193 | |
| | 194 | * '''Е-mail Валидација преку Регуларен Израз (RegEx):''' Во табелата `"User"`, внесот на е-маил адресата е строго контролиран на ниво на база со `CHECK (Email ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')`. Ова спречува невалидни податоци уште пред да стигнат до апликативниот слој. |
| | 195 | * '''Интегритет на претплати (`ON DELETE RESTRICT`):''' За разлика од другите табели каде бришењето оди каскадно (`CASCADE`), кај `User_Subscription` кон `Subscription` е ставено `RESTRICT`. Тоа значи дека системот нема да дозволи да се избрише одреден претплатен пакет (на пр. "Premium") ако постојат активни корисници кои моментално го плаќаат и користат тој пакет. |
| | 196 | * '''Униформно следење на содржини преку `Watchable`:''' Посредничката логика во `Watchable` овозможува `WatchHistory` да не се грижи дали корисникот гледал филм или единечна епизода од серија – двата типа се сведуваат на ист апстрактен ID со времетраење, што ја прави базата исклучително скалабилна за идни проширувања. |