From fcee0f9213ef6cb0b2c6f35008dc00c3fb1b262e Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Wed, 26 Aug 2026 22:16:56 -0400 Subject: [PATCH] Organise ddl statements into separate files per create table (#251) --- .../Database/Database.Migrations/schema.dbml | 238 +++++++ .../scripts/01-schema/01-UserAccount.sql | 31 + .../scripts/01-schema/02-Photo.sql | 27 + .../scripts/01-schema/03-UserAvatar.sql | 29 + .../scripts/01-schema/04-UserVerification.sql | 27 + .../scripts/01-schema/05-UserCredential.sql | 40 ++ .../scripts/01-schema/06-UserFollow.sql | 39 ++ .../scripts/01-schema/07-Country.sql | 17 + .../scripts/01-schema/08-StateProvince.sql | 28 + .../scripts/01-schema/09-City.sql | 22 + .../scripts/01-schema/10-BreweryPost.sql | 30 + .../01-schema/11-BreweryPostLocation.sql | 54 ++ .../scripts/01-schema/12-BreweryPostPhoto.sql | 35 + .../scripts/01-schema/13-BeerStyle.sql | 17 + .../scripts/01-schema/14-BeerPost.sql | 62 ++ .../scripts/01-schema/15-BeerPostPhoto.sql | 35 + .../scripts/01-schema/16-BeerPostComment.sql | 43 ++ .../scripts/01-schema/schema.sql | 617 ------------------ 18 files changed, 774 insertions(+), 617 deletions(-) create mode 100644 web/backend/Database/Database.Migrations/schema.dbml create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/01-UserAccount.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/02-Photo.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/03-UserAvatar.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/04-UserVerification.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/05-UserCredential.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/06-UserFollow.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/07-Country.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/08-StateProvince.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/09-City.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/10-BreweryPost.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/11-BreweryPostLocation.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/12-BreweryPostPhoto.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/13-BeerStyle.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/14-BeerPost.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/15-BeerPostPhoto.sql create mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/16-BeerPostComment.sql delete mode 100644 web/backend/Database/Database.Migrations/scripts/01-schema/schema.sql diff --git a/web/backend/Database/Database.Migrations/schema.dbml b/web/backend/Database/Database.Migrations/schema.dbml new file mode 100644 index 00000000..f11e88fc --- /dev/null +++ b/web/backend/Database/Database.Migrations/schema.dbml @@ -0,0 +1,238 @@ +Project biergarten_app { + database_type: 'SQL Server' + Note: 'Generated from Database.Migrations/scripts/01-schema. Default schema is dbo.' +} + +Table UserAccount { + UserAccountID uniqueidentifier [pk, default: `newid()`] + Username varchar(64) [not null, unique] + FirstName nvarchar(128) [not null] + LastName nvarchar(128) [not null] + Email varchar(128) [not null, unique] + CreatedAt datetime [not null, default: `getdate()`] + UpdatedAt datetime + DateOfBirth date [not null] + RowVersion rowversion +} + +Table Photo { + PhotoID uniqueidentifier [pk, default: `newid()`] + Hyperlink nvarchar(256) [note: 'storage handled via filesystem/cloud service'] + UploadedByID uniqueidentifier [not null] + UploadedAt datetime [not null, default: `getdate()`] + RowVersion rowversion + + Note: 'A user account cannot be deleted while it has uploaded photos' + + Indexes { + UploadedByID + } +} + +Table UserAvatar { + UserAvatarID uniqueidentifier [pk, default: `newid()`] + UserAccountID uniqueidentifier [not null, unique] + PhotoID uniqueidentifier [not null] + RowVersion rowversion + + Note: 'Deleted when the user account is deleted' +} + +Table UserVerification { + UserVerificationID uniqueidentifier [pk, default: `newid()`] + UserAccountID uniqueidentifier [not null, unique] + VerificationDateTime datetime [not null, default: `getdate()`] + RowVersion rowversion + + Note: 'Deleted when the user account is deleted' +} + +Table UserCredential { + UserCredentialID uniqueidentifier [pk, default: `newid()`] + UserAccountID uniqueidentifier [not null] + CreatedAt datetime [not null, default: `getdate()`] + Expiry datetime [not null, default: `dateadd(day, 90, getdate())`] + Hash nvarchar(256) [not null, note: 'argon2 hash'] + IsRevoked bit [not null, default: 0] + RevokedAt datetime + RowVersion rowversion + + Note: 'Deleted when the user account is deleted' + + Indexes { + UserAccountID + (UserAccountID, IsRevoked, Expiry) [name: 'IX_UserCredential_Account_Active', note: 'includes Hash'] + } +} + +Table UserFollow { + UserFollowID uniqueidentifier [pk, default: `newid()`] + UserAccountID uniqueidentifier [not null] + FollowingID uniqueidentifier [not null] + CreatedAt datetime [not null, default: `getdate()`] + RowVersion rowversion + + Note: 'CHECK: UserAccountID != FollowingID (cannot follow own account)' + + Indexes { + (UserAccountID, FollowingID) + (FollowingID, UserAccountID) + } +} + +Table Country { + CountryID uniqueidentifier [pk, default: `newid()`] + CountryName nvarchar(100) [not null] + ISO3166_1 char(2) [not null, unique] + RowVersion rowversion +} + +Table StateProvince { + StateProvinceID uniqueidentifier [pk, default: `newid()`] + StateProvinceName nvarchar(100) [not null] + ISO3166_2 char(6) [not null, unique, note: "e.g. 'US-CA' for California, 'CA-ON' for Ontario"] + CountryID uniqueidentifier [not null] + RowVersion rowversion + + Indexes { + CountryID + } +} + +Table City { + CityID uniqueidentifier [pk, default: `newid()`] + CityName nvarchar(100) [not null] + StateProvinceID uniqueidentifier [not null] + RowVersion rowversion + + Indexes { + StateProvinceID + } +} + +Table BreweryPost { + BreweryPostID uniqueidentifier [pk, default: `newid()`] + BreweryName nvarchar(256) [not null] + PostedByID uniqueidentifier [not null] + Description "nvarchar(max)" [not null] + CreatedAt datetime [not null, default: `getdate()`] + UpdatedAt datetime + RowVersion rowversion + + Note: 'A user cannot be deleted if they have a post' + + Indexes { + PostedByID + } +} + +Table BreweryPostLocation { + BreweryPostLocationID uniqueidentifier [pk, default: `newid()`] + BreweryPostID uniqueidentifier [not null, unique] + AddressLine1 nvarchar(256) [not null] + AddressLine2 nvarchar(256) + PostalCode nvarchar(20) [not null] + CityID uniqueidentifier [not null] + Coordinates geography [note: 'spatial index deferred, see schema.sql history'] + RowVersion rowversion + + Indexes { + BreweryPostID + CityID + } +} + +Table BreweryPostPhoto { + BreweryPostPhotoID uniqueidentifier [pk, default: `newid()`] + BreweryPostID uniqueidentifier [not null] + PhotoID uniqueidentifier [not null] + LinkedAt datetime [not null, default: `getdate()`] + RowVersion rowversion + + Note: 'All photos linked to a post are deleted if the post is deleted' + + Indexes { + (PhotoID, BreweryPostID) + (BreweryPostID, PhotoID) + } +} + +Table BeerStyle { + BeerStyleID uniqueidentifier [pk, default: `newid()`] + StyleName nvarchar(100) [not null, unique] + Description "nvarchar(max)" + RowVersion rowversion +} + +Table BeerPost { + BeerPostID uniqueidentifier [pk, default: `newid()`] + Name nvarchar(100) [not null] + Description "nvarchar(max)" [not null] + ABV "decimal(4,2)" [not null, note: 'CHECK ABV BETWEEN 0 AND 67'] + IBU int [not null, note: 'CHECK IBU BETWEEN 0 AND 120'] + PostedByID uniqueidentifier [not null] + BeerStyleID uniqueidentifier [not null] + BrewedByID uniqueidentifier [not null] + CreatedAt datetime [not null, default: `getdate()`] + UpdatedAt datetime + RowVersion rowversion + + Indexes { + PostedByID + BeerStyleID + BrewedByID + } +} + +Table BeerPostPhoto { + BeerPostPhotoID uniqueidentifier [pk, default: `newid()`] + BeerPostID uniqueidentifier [not null] + PhotoID uniqueidentifier [not null] + LinkedAt datetime [not null, default: `getdate()`] + RowVersion rowversion + + Note: 'All photos linked to a beer post are deleted if the post is deleted' + + Indexes { + (PhotoID, BeerPostID) + (BeerPostID, PhotoID) + } +} + +Table BeerPostComment { + BeerPostCommentID uniqueidentifier [pk, default: `newid()`] + Comment nvarchar(250) [not null] + BeerPostID uniqueidentifier [not null] + CommentedByID uniqueidentifier [not null] + Rating int [not null, note: 'CHECK Rating BETWEEN 1 AND 5'] + CreatedAt datetime [not null, default: `getdate()`] + UpdatedAt datetime + RowVersion rowversion + + Indexes { + BeerPostID + CommentedByID + } +} + +Ref: Photo.UploadedByID > UserAccount.UserAccountID [delete: no action] +Ref: UserAvatar.UserAccountID - UserAccount.UserAccountID [delete: cascade] +Ref: UserAvatar.PhotoID > Photo.PhotoID +Ref: UserVerification.UserAccountID - UserAccount.UserAccountID [delete: cascade] +Ref: UserCredential.UserAccountID > UserAccount.UserAccountID [delete: cascade] +Ref: UserFollow.UserAccountID > UserAccount.UserAccountID [delete: no action] +Ref: UserFollow.FollowingID > UserAccount.UserAccountID [delete: no action] +Ref: StateProvince.CountryID > Country.CountryID +Ref: City.StateProvinceID > StateProvince.StateProvinceID +Ref: BreweryPost.PostedByID > UserAccount.UserAccountID [delete: no action] +Ref: BreweryPostLocation.BreweryPostID - BreweryPost.BreweryPostID [delete: cascade] +Ref: BreweryPostLocation.CityID > City.CityID +Ref: BreweryPostPhoto.BreweryPostID > BreweryPost.BreweryPostID [delete: cascade] +Ref: BreweryPostPhoto.PhotoID > Photo.PhotoID [delete: cascade] +Ref: BeerPost.PostedByID > UserAccount.UserAccountID [delete: no action] +Ref: BeerPost.BeerStyleID > BeerStyle.BeerStyleID +Ref: BeerPost.BrewedByID > BreweryPost.BreweryPostID +Ref: BeerPostPhoto.BeerPostID > BeerPost.BeerPostID [delete: cascade] +Ref: BeerPostPhoto.PhotoID > Photo.PhotoID [delete: cascade] +Ref: BeerPostComment.BeerPostID > BeerPost.BeerPostID +Ref: BeerPostComment.CommentedByID > UserAccount.UserAccountID [delete: no action] diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/01-UserAccount.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/01-UserAccount.sql new file mode 100644 index 00000000..0d482939 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/01-UserAccount.sql @@ -0,0 +1,31 @@ +CREATE TABLE dbo.UserAccount +( + UserAccountID UNIQUEIDENTIFIER + CONSTRAINT DF_UserAccountID DEFAULT NEWID(), + + Username VARCHAR(64) NOT NULL, + + FirstName NVARCHAR(128) NOT NULL, + + LastName NVARCHAR(128) NOT NULL, + + Email VARCHAR(128) NOT NULL, + + CreatedAt DATETIME NOT NULL + CONSTRAINT DF_UserAccount_CreatedAt DEFAULT GETDATE(), + + UpdatedAt DATETIME, + + DateOfBirth DATE NOT NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_UserAccount + PRIMARY KEY (UserAccountID), + + CONSTRAINT AK_Username + UNIQUE (Username), + + CONSTRAINT AK_Email + UNIQUE (Email) +); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/02-Photo.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/02-Photo.sql new file mode 100644 index 00000000..d7f6d2cc --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/02-Photo.sql @@ -0,0 +1,27 @@ +CREATE TABLE Photo -- All photos must be linked to a user account, you cannot delete a user account if they have uploaded photos +( + PhotoID UNIQUEIDENTIFIER + CONSTRAINT DF_PhotoID DEFAULT NEWID(), + + Hyperlink NVARCHAR(256), + -- storage is handled via filesystem or cloud service + + UploadedByID UNIQUEIDENTIFIER NOT NULL, + + UploadedAt DATETIME NOT NULL + CONSTRAINT DF_Photo_UploadedAt DEFAULT GETDATE(), + + RowVersion ROWVERSION, + + CONSTRAINT PK_Photo + PRIMARY KEY (PhotoID), + + CONSTRAINT FK_Photo_UploadedBy + FOREIGN KEY (UploadedByID) + REFERENCES UserAccount (UserAccountID) + ON DELETE NO ACTION +); + +CREATE +NONCLUSTERED INDEX IX_Photo_UploadedByID + ON Photo(UploadedByID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/03-UserAvatar.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/03-UserAvatar.sql new file mode 100644 index 00000000..7af3198d --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/03-UserAvatar.sql @@ -0,0 +1,29 @@ +CREATE TABLE UserAvatar -- delete avatar photo when user account is deleted +( + UserAvatarID UNIQUEIDENTIFIER + CONSTRAINT DF_UserAvatarID DEFAULT NEWID(), + + UserAccountID UNIQUEIDENTIFIER NOT NULL, + + PhotoID UNIQUEIDENTIFIER NOT NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_UserAvatar PRIMARY KEY (UserAvatarID), + + CONSTRAINT FK_UserAvatar_UserAccount + FOREIGN KEY (UserAccountID) + REFERENCES UserAccount (UserAccountID) + ON DELETE CASCADE, + + CONSTRAINT FK_UserAvatar_PhotoID + FOREIGN KEY (PhotoID) + REFERENCES Photo (PhotoID), + + CONSTRAINT AK_UserAvatar_UserAccountID + UNIQUE (UserAccountID) +); + +CREATE +NONCLUSTERED INDEX IX_UserAvatar_UserAccount + ON UserAvatar(UserAccountID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/04-UserVerification.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/04-UserVerification.sql new file mode 100644 index 00000000..9febb3d9 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/04-UserVerification.sql @@ -0,0 +1,27 @@ +CREATE TABLE UserVerification -- delete verification data when user account is deleted +( + UserVerificationID UNIQUEIDENTIFIER + CONSTRAINT DF_UserVerificationID DEFAULT NEWID(), + + UserAccountID UNIQUEIDENTIFIER NOT NULL, + + VerificationDateTime DATETIME NOT NULL + CONSTRAINT DF_VerificationDateTime DEFAULT GETDATE(), + + RowVersion ROWVERSION, + + CONSTRAINT PK_UserVerification + PRIMARY KEY (UserVerificationID), + + CONSTRAINT FK_UserVerification_UserAccount + FOREIGN KEY (UserAccountID) + REFERENCES UserAccount (UserAccountID) + ON DELETE CASCADE, + + CONSTRAINT AK_UserVerification_UserAccountID + UNIQUE (UserAccountID) +); + +CREATE +NONCLUSTERED INDEX IX_UserVerification_UserAccount + ON UserVerification(UserAccountID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/05-UserCredential.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/05-UserCredential.sql new file mode 100644 index 00000000..17cef1e1 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/05-UserCredential.sql @@ -0,0 +1,40 @@ +CREATE TABLE UserCredential -- delete credentials when user account is deleted +( + UserCredentialID UNIQUEIDENTIFIER + CONSTRAINT DF_UserCredentialID DEFAULT NEWID(), + + UserAccountID UNIQUEIDENTIFIER NOT NULL, + + CreatedAt DATETIME NOT NULL + CONSTRAINT DF_UserCredential_CreatedAt DEFAULT GETDATE(), + + Expiry DATETIME NOT NULL + CONSTRAINT DF_UserCredential_Expiry DEFAULT DATEADD(DAY, 90, GETDATE()), + + Hash NVARCHAR(256) NOT NULL, + -- uses argon2 + + IsRevoked BIT NOT NULL + CONSTRAINT DF_UserCredential_IsRevoked DEFAULT 0, + + RevokedAt DATETIME NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_UserCredential + PRIMARY KEY (UserCredentialID), + + CONSTRAINT FK_UserCredential_UserAccount + FOREIGN KEY (UserAccountID) + REFERENCES UserAccount (UserAccountID) + ON DELETE CASCADE +); + +CREATE +NONCLUSTERED INDEX IX_UserCredential_UserAccount + ON UserCredential(UserAccountID); + +CREATE +NONCLUSTERED INDEX IX_UserCredential_Account_Active + ON UserCredential(UserAccountID, IsRevoked, Expiry) + INCLUDE (Hash); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/06-UserFollow.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/06-UserFollow.sql new file mode 100644 index 00000000..5e9d06a8 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/06-UserFollow.sql @@ -0,0 +1,39 @@ +CREATE TABLE UserFollow +( + UserFollowID UNIQUEIDENTIFIER + CONSTRAINT DF_UserFollowID DEFAULT NEWID(), + + UserAccountID UNIQUEIDENTIFIER NOT NULL, + + FollowingID UNIQUEIDENTIFIER NOT NULL, + + CreatedAt DATETIME NOT NULL + CONSTRAINT DF_UserFollow_CreatedAt DEFAULT GETDATE(), + + RowVersion ROWVERSION, + + CONSTRAINT PK_UserFollow + PRIMARY KEY (UserFollowID), + + CONSTRAINT FK_UserFollow_UserAccount + FOREIGN KEY (UserAccountID) + REFERENCES UserAccount (UserAccountID) + ON DELETE NO ACTION, + + CONSTRAINT FK_UserFollow_UserAccountFollowing + FOREIGN KEY (FollowingID) + REFERENCES UserAccount (UserAccountID) + ON DELETE NO ACTION, + + CONSTRAINT CK_CannotFollowOwnAccount + CHECK (UserAccountID != FollowingID +) +); + +CREATE +NONCLUSTERED INDEX IX_UserFollow_UserAccount_FollowingID + ON UserFollow(UserAccountID, FollowingID); + +CREATE +NONCLUSTERED INDEX IX_UserFollow_FollowingID_UserAccount + ON UserFollow(FollowingID, UserAccountID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/07-Country.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/07-Country.sql new file mode 100644 index 00000000..07a727b0 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/07-Country.sql @@ -0,0 +1,17 @@ +CREATE TABLE Country +( + CountryID UNIQUEIDENTIFIER + CONSTRAINT DF_CountryID DEFAULT NEWID(), + + CountryName NVARCHAR(100) NOT NULL, + + ISO3166_1 CHAR(2) NOT NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_Country + PRIMARY KEY (CountryID), + + CONSTRAINT AK_Country_ISO3166_1 + UNIQUE (ISO3166_1) +); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/08-StateProvince.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/08-StateProvince.sql new file mode 100644 index 00000000..d91526c3 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/08-StateProvince.sql @@ -0,0 +1,28 @@ +CREATE TABLE StateProvince +( + StateProvinceID UNIQUEIDENTIFIER + CONSTRAINT DF_StateProvinceID DEFAULT NEWID(), + + StateProvinceName NVARCHAR(100) NOT NULL, + + ISO3166_2 CHAR(6) NOT NULL, + -- eg 'US-CA' for California, 'CA-ON' for Ontario + + CountryID UNIQUEIDENTIFIER NOT NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_StateProvince + PRIMARY KEY (StateProvinceID), + + CONSTRAINT AK_StateProvince_ISO3166_2 + UNIQUE (ISO3166_2), + + CONSTRAINT FK_StateProvince_Country + FOREIGN KEY (CountryID) + REFERENCES Country (CountryID) +); + +CREATE +NONCLUSTERED INDEX IX_StateProvince_Country + ON StateProvince(CountryID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/09-City.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/09-City.sql new file mode 100644 index 00000000..ee2cce0f --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/09-City.sql @@ -0,0 +1,22 @@ +CREATE TABLE City +( + CityID UNIQUEIDENTIFIER + CONSTRAINT DF_CityID DEFAULT NEWID(), + + CityName NVARCHAR(100) NOT NULL, + + StateProvinceID UNIQUEIDENTIFIER NOT NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_City + PRIMARY KEY (CityID), + + CONSTRAINT FK_City_StateProvince + FOREIGN KEY (StateProvinceID) + REFERENCES StateProvince (StateProvinceID) +); + +CREATE +NONCLUSTERED INDEX IX_City_StateProvince + ON City(StateProvinceID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/10-BreweryPost.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/10-BreweryPost.sql new file mode 100644 index 00000000..4a8fc640 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/10-BreweryPost.sql @@ -0,0 +1,30 @@ +CREATE TABLE BreweryPost -- A user cannot be deleted if they have a post +( + BreweryPostID UNIQUEIDENTIFIER + CONSTRAINT DF_BreweryPostID DEFAULT NEWID(), + + BreweryName NVARCHAR(256) NOT NULL, + + PostedByID UNIQUEIDENTIFIER NOT NULL, + + Description NVARCHAR(MAX) NOT NULL, + + CreatedAt DATETIME NOT NULL + CONSTRAINT DF_BreweryPost_CreatedAt DEFAULT GETDATE(), + + UpdatedAt DATETIME NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_BreweryPost + PRIMARY KEY (BreweryPostID), + + CONSTRAINT FK_BreweryPost_UserAccount + FOREIGN KEY (PostedByID) + REFERENCES UserAccount (UserAccountID) + ON DELETE NO ACTION +); + +CREATE +NONCLUSTERED INDEX IX_BreweryPost_PostedByID + ON BreweryPost(PostedByID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/11-BreweryPostLocation.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/11-BreweryPostLocation.sql new file mode 100644 index 00000000..2b6cf591 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/11-BreweryPostLocation.sql @@ -0,0 +1,54 @@ +CREATE TABLE BreweryPostLocation +( + BreweryPostLocationID UNIQUEIDENTIFIER + CONSTRAINT DF_BreweryPostLocationID DEFAULT NEWID(), + + BreweryPostID UNIQUEIDENTIFIER NOT NULL, + + AddressLine1 NVARCHAR(256) NOT NULL, + + AddressLine2 NVARCHAR(256), + + PostalCode NVARCHAR(20) NOT NULL, + + CityID UNIQUEIDENTIFIER NOT NULL, + + Coordinates GEOGRAPHY NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_BreweryPostLocation + PRIMARY KEY (BreweryPostLocationID), + + CONSTRAINT AK_BreweryPostLocation_BreweryPostID + UNIQUE (BreweryPostID), + + CONSTRAINT FK_BreweryPostLocation_BreweryPost + FOREIGN KEY (BreweryPostID) + REFERENCES BreweryPost (BreweryPostID) + ON DELETE CASCADE, + + CONSTRAINT FK_BreweryPostLocation_City + FOREIGN KEY (CityID) + REFERENCES City (CityID) +); + +CREATE +NONCLUSTERED INDEX IX_BreweryPostLocation_BreweryPost + ON BreweryPostLocation(BreweryPostID); + +CREATE +NONCLUSTERED INDEX IX_BreweryPostLocation_City + ON BreweryPostLocation(CityID); + +-- To assess when the time comes: + +-- This would allow for efficient spatial queries to find breweries within a certain distance of a location, but it adds overhead to insert/update operations. + +-- CREATE SPATIAL INDEX SIDX_BreweryPostLocation_Coordinates +-- ON BreweryPostLocation(Coordinates) +-- USING GEOGRAPHY_GRID +-- WITH ( +-- GRIDS = (LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM), +-- CELLS_PER_OBJECT = 16 +-- ); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/12-BreweryPostPhoto.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/12-BreweryPostPhoto.sql new file mode 100644 index 00000000..e6407b3f --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/12-BreweryPostPhoto.sql @@ -0,0 +1,35 @@ +CREATE TABLE BreweryPostPhoto -- All photos linked to a post are deleted if the post is deleted +( + BreweryPostPhotoID UNIQUEIDENTIFIER + CONSTRAINT DF_BreweryPostPhotoID DEFAULT NEWID(), + + BreweryPostID UNIQUEIDENTIFIER NOT NULL, + + PhotoID UNIQUEIDENTIFIER NOT NULL, + + LinkedAt DATETIME NOT NULL + CONSTRAINT DF_BreweryPostPhoto_LinkedAt DEFAULT GETDATE(), + + RowVersion ROWVERSION, + + CONSTRAINT PK_BreweryPostPhoto + PRIMARY KEY (BreweryPostPhotoID), + + CONSTRAINT FK_BreweryPostPhoto_BreweryPost + FOREIGN KEY (BreweryPostID) + REFERENCES BreweryPost (BreweryPostID) + ON DELETE CASCADE, + + CONSTRAINT FK_BreweryPostPhoto_Photo + FOREIGN KEY (PhotoID) + REFERENCES Photo (PhotoID) + ON DELETE CASCADE +); + +CREATE +NONCLUSTERED INDEX IX_BreweryPostPhoto_Photo_BreweryPost + ON BreweryPostPhoto(PhotoID, BreweryPostID); + +CREATE +NONCLUSTERED INDEX IX_BreweryPostPhoto_BreweryPost_Photo + ON BreweryPostPhoto(BreweryPostID, PhotoID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/13-BeerStyle.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/13-BeerStyle.sql new file mode 100644 index 00000000..484b6654 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/13-BeerStyle.sql @@ -0,0 +1,17 @@ +CREATE TABLE BeerStyle +( + BeerStyleID UNIQUEIDENTIFIER + CONSTRAINT DF_BeerStyleID DEFAULT NEWID(), + + StyleName NVARCHAR(100) NOT NULL, + + Description NVARCHAR(MAX), + + RowVersion ROWVERSION, + + CONSTRAINT PK_BeerStyle + PRIMARY KEY (BeerStyleID), + + CONSTRAINT AK_BeerStyle_StyleName + UNIQUE (StyleName) +); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/14-BeerPost.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/14-BeerPost.sql new file mode 100644 index 00000000..4a70a826 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/14-BeerPost.sql @@ -0,0 +1,62 @@ +CREATE TABLE BeerPost +( + BeerPostID UNIQUEIDENTIFIER + CONSTRAINT DF_BeerPostID DEFAULT NEWID(), + + Name NVARCHAR(100) NOT NULL, + + Description NVARCHAR(MAX) NOT NULL, + + ABV DECIMAL(4, 2) NOT NULL, + -- Alcohol By Volume (typically 0-67%) + + IBU INT NOT NULL, + -- International Bitterness Units (typically 0-120) + + PostedByID UNIQUEIDENTIFIER NOT NULL, + + BeerStyleID UNIQUEIDENTIFIER NOT NULL, + + BrewedByID UNIQUEIDENTIFIER NOT NULL, + + CreatedAt DATETIME NOT NULL + CONSTRAINT DF_BeerPost_CreatedAt DEFAULT GETDATE(), + + UpdatedAt DATETIME, + + RowVersion ROWVERSION, + + CONSTRAINT PK_BeerPost + PRIMARY KEY (BeerPostID), + + CONSTRAINT FK_BeerPost_PostedBy + FOREIGN KEY (PostedByID) + REFERENCES UserAccount (UserAccountID) + ON DELETE NO ACTION, + + CONSTRAINT FK_BeerPost_BeerStyle + FOREIGN KEY (BeerStyleID) + REFERENCES BeerStyle (BeerStyleID), + + CONSTRAINT FK_BeerPost_Brewery + FOREIGN KEY (BrewedByID) + REFERENCES BreweryPost (BreweryPostID), + + CONSTRAINT CHK_BeerPost_ABV + CHECK (ABV >= 0 AND ABV <= 67), + + CONSTRAINT CHK_BeerPost_IBU + CHECK (IBU >= 0 AND IBU <= 120) +); + +CREATE +NONCLUSTERED INDEX IX_BeerPost_PostedBy + ON BeerPost(PostedByID); + +CREATE +NONCLUSTERED INDEX IX_BeerPost_BeerStyle + ON BeerPost(BeerStyleID); + +CREATE +NONCLUSTERED INDEX IX_BeerPost_BrewedBy + ON BeerPost(BrewedByID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/15-BeerPostPhoto.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/15-BeerPostPhoto.sql new file mode 100644 index 00000000..415de2f2 --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/15-BeerPostPhoto.sql @@ -0,0 +1,35 @@ +CREATE TABLE BeerPostPhoto -- All photos linked to a beer post are deleted if the post is deleted +( + BeerPostPhotoID UNIQUEIDENTIFIER + CONSTRAINT DF_BeerPostPhotoID DEFAULT NEWID(), + + BeerPostID UNIQUEIDENTIFIER NOT NULL, + + PhotoID UNIQUEIDENTIFIER NOT NULL, + + LinkedAt DATETIME NOT NULL + CONSTRAINT DF_BeerPostPhoto_LinkedAt DEFAULT GETDATE(), + + RowVersion ROWVERSION, + + CONSTRAINT PK_BeerPostPhoto + PRIMARY KEY (BeerPostPhotoID), + + CONSTRAINT FK_BeerPostPhoto_BeerPost + FOREIGN KEY (BeerPostID) + REFERENCES BeerPost (BeerPostID) + ON DELETE CASCADE, + + CONSTRAINT FK_BeerPostPhoto_Photo + FOREIGN KEY (PhotoID) + REFERENCES Photo (PhotoID) + ON DELETE CASCADE +); + +CREATE +NONCLUSTERED INDEX IX_BeerPostPhoto_Photo_BeerPost + ON BeerPostPhoto(PhotoID, BeerPostID); + +CREATE +NONCLUSTERED INDEX IX_BeerPostPhoto_BeerPost_Photo + ON BeerPostPhoto(BeerPostID, PhotoID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/16-BeerPostComment.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/16-BeerPostComment.sql new file mode 100644 index 00000000..3e0b386e --- /dev/null +++ b/web/backend/Database/Database.Migrations/scripts/01-schema/16-BeerPostComment.sql @@ -0,0 +1,43 @@ +CREATE TABLE BeerPostComment +( + BeerPostCommentID UNIQUEIDENTIFIER + CONSTRAINT DF_BeerPostComment DEFAULT NEWID(), + + Comment NVARCHAR(250) NOT NULL, + + BeerPostID UNIQUEIDENTIFIER NOT NULL, + + CommentedByID UNIQUEIDENTIFIER NOT NULL, + + Rating INT NOT NULL, + + CreatedAt DATETIME NOT NULL + CONSTRAINT DF_BeerPostComment_CreatedAt DEFAULT GETDATE(), + + UpdatedAt DATETIME NULL, + + RowVersion ROWVERSION, + + CONSTRAINT PK_BeerPostComment + PRIMARY KEY (BeerPostCommentID), + + CONSTRAINT FK_BeerPostComment_BeerPost + FOREIGN KEY (BeerPostID) + REFERENCES BeerPost (BeerPostID), + + CONSTRAINT FK_BeerPostComment_UserAccount + FOREIGN KEY (CommentedByID) + REFERENCES UserAccount (UserAccountID) + ON DELETE NO ACTION, + + CONSTRAINT CHK_BeerPostComment_Rating + CHECK (Rating BETWEEN 1 AND 5) +); + +CREATE +NONCLUSTERED INDEX IX_BeerPostComment_BeerPost + ON BeerPostComment(BeerPostID); + +CREATE +NONCLUSTERED INDEX IX_BeerPostComment_CommentedBy + ON BeerPostComment(CommentedByID); diff --git a/web/backend/Database/Database.Migrations/scripts/01-schema/schema.sql b/web/backend/Database/Database.Migrations/scripts/01-schema/schema.sql deleted file mode 100644 index f024b05a..00000000 --- a/web/backend/Database/Database.Migrations/scripts/01-schema/schema.sql +++ /dev/null @@ -1,617 +0,0 @@ --- ---------------------------------------------------------------------------- --- ---------------------------------------------------------------------------- -/* -USE master; - -IF EXISTS (SELECT name -FROM sys.databases -WHERE name = N'Biergarten') -BEGIN - ALTER DATABASE Biergarten SET SINGLE_USER WITH ROLLBACK IMMEDIATE; -END - -DROP DATABASE IF EXISTS Biergarten; - -CREATE DATABASE Biergarten; - -USE Biergarten; -*/ --- ---------------------------------------------------------------------------- --- ---------------------------------------------------------------------------- - -CREATE TABLE dbo.UserAccount -( - UserAccountID UNIQUEIDENTIFIER - CONSTRAINT DF_UserAccountID DEFAULT NEWID(), - - Username VARCHAR(64) NOT NULL, - - FirstName NVARCHAR(128) NOT NULL, - - LastName NVARCHAR(128) NOT NULL, - - Email VARCHAR(128) NOT NULL, - - CreatedAt DATETIME NOT NULL - CONSTRAINT DF_UserAccount_CreatedAt DEFAULT GETDATE(), - - UpdatedAt DATETIME, - - DateOfBirth DATE NOT NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_UserAccount - PRIMARY KEY (UserAccountID), - - CONSTRAINT AK_Username - UNIQUE (Username), - - CONSTRAINT AK_Email - UNIQUE (Email) -); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE Photo -- All photos must be linked to a user account, you cannot delete a user account if they have uploaded photos -( - PhotoID UNIQUEIDENTIFIER - CONSTRAINT DF_PhotoID DEFAULT NEWID(), - - Hyperlink NVARCHAR(256), - -- storage is handled via filesystem or cloud service - - UploadedByID UNIQUEIDENTIFIER NOT NULL, - - UploadedAt DATETIME NOT NULL - CONSTRAINT DF_Photo_UploadedAt DEFAULT GETDATE(), - - RowVersion ROWVERSION, - - CONSTRAINT PK_Photo - PRIMARY KEY (PhotoID), - - CONSTRAINT FK_Photo_UploadedBy - FOREIGN KEY (UploadedByID) - REFERENCES UserAccount (UserAccountID) - ON DELETE NO ACTION -); - -CREATE -NONCLUSTERED INDEX IX_Photo_UploadedByID - ON Photo(UploadedByID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE UserAvatar -- delete avatar photo when user account is deleted -( - UserAvatarID UNIQUEIDENTIFIER - CONSTRAINT DF_UserAvatarID DEFAULT NEWID(), - - UserAccountID UNIQUEIDENTIFIER NOT NULL, - - PhotoID UNIQUEIDENTIFIER NOT NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_UserAvatar PRIMARY KEY (UserAvatarID), - - CONSTRAINT FK_UserAvatar_UserAccount - FOREIGN KEY (UserAccountID) - REFERENCES UserAccount (UserAccountID) - ON DELETE CASCADE, - - CONSTRAINT FK_UserAvatar_PhotoID - FOREIGN KEY (PhotoID) - REFERENCES Photo (PhotoID), - - CONSTRAINT AK_UserAvatar_UserAccountID - UNIQUE (UserAccountID) -); - -CREATE -NONCLUSTERED INDEX IX_UserAvatar_UserAccount - ON UserAvatar(UserAccountID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE UserVerification -- delete verification data when user account is deleted -( - UserVerificationID UNIQUEIDENTIFIER - CONSTRAINT DF_UserVerificationID DEFAULT NEWID(), - - UserAccountID UNIQUEIDENTIFIER NOT NULL, - - VerificationDateTime DATETIME NOT NULL - CONSTRAINT DF_VerificationDateTime DEFAULT GETDATE(), - - RowVersion ROWVERSION, - - CONSTRAINT PK_UserVerification - PRIMARY KEY (UserVerificationID), - - CONSTRAINT FK_UserVerification_UserAccount - FOREIGN KEY (UserAccountID) - REFERENCES UserAccount (UserAccountID) - ON DELETE CASCADE, - - CONSTRAINT AK_UserVerification_UserAccountID - UNIQUE (UserAccountID) -); - -CREATE -NONCLUSTERED INDEX IX_UserVerification_UserAccount - ON UserVerification(UserAccountID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE UserCredential -- delete credentials when user account is deleted -( - UserCredentialID UNIQUEIDENTIFIER - CONSTRAINT DF_UserCredentialID DEFAULT NEWID(), - - UserAccountID UNIQUEIDENTIFIER NOT NULL, - - CreatedAt DATETIME NOT NULL - CONSTRAINT DF_UserCredential_CreatedAt DEFAULT GETDATE(), - - Expiry DATETIME NOT NULL - CONSTRAINT DF_UserCredential_Expiry DEFAULT DATEADD(DAY, 90, GETDATE()), - - Hash NVARCHAR(256) NOT NULL, - -- uses argon2 - - IsRevoked BIT NOT NULL - CONSTRAINT DF_UserCredential_IsRevoked DEFAULT 0, - - RevokedAt DATETIME NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_UserCredential - PRIMARY KEY (UserCredentialID), - - CONSTRAINT FK_UserCredential_UserAccount - FOREIGN KEY (UserAccountID) - REFERENCES UserAccount (UserAccountID) - ON DELETE CASCADE -); - -CREATE -NONCLUSTERED INDEX IX_UserCredential_UserAccount - ON UserCredential(UserAccountID); - -CREATE -NONCLUSTERED INDEX IX_UserCredential_Account_Active - ON UserCredential(UserAccountID, IsRevoked, Expiry) - INCLUDE (Hash); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE UserFollow -( - UserFollowID UNIQUEIDENTIFIER - CONSTRAINT DF_UserFollowID DEFAULT NEWID(), - - UserAccountID UNIQUEIDENTIFIER NOT NULL, - - FollowingID UNIQUEIDENTIFIER NOT NULL, - - CreatedAt DATETIME NOT NULL - CONSTRAINT DF_UserFollow_CreatedAt DEFAULT GETDATE(), - - RowVersion ROWVERSION, - - CONSTRAINT PK_UserFollow - PRIMARY KEY (UserFollowID), - - CONSTRAINT FK_UserFollow_UserAccount - FOREIGN KEY (UserAccountID) - REFERENCES UserAccount (UserAccountID) - ON DELETE NO ACTION, - - CONSTRAINT FK_UserFollow_UserAccountFollowing - FOREIGN KEY (FollowingID) - REFERENCES UserAccount (UserAccountID) - ON DELETE NO ACTION, - - CONSTRAINT CK_CannotFollowOwnAccount - CHECK (UserAccountID != FollowingID -) -); - -CREATE -NONCLUSTERED INDEX IX_UserFollow_UserAccount_FollowingID - ON UserFollow(UserAccountID, FollowingID); - -CREATE -NONCLUSTERED INDEX IX_UserFollow_FollowingID_UserAccount - ON UserFollow(FollowingID, UserAccountID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE Country -( - CountryID UNIQUEIDENTIFIER - CONSTRAINT DF_CountryID DEFAULT NEWID(), - - CountryName NVARCHAR(100) NOT NULL, - - ISO3166_1 CHAR(2) NOT NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_Country - PRIMARY KEY (CountryID), - - CONSTRAINT AK_Country_ISO3166_1 - UNIQUE (ISO3166_1) -); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE StateProvince -( - StateProvinceID UNIQUEIDENTIFIER - CONSTRAINT DF_StateProvinceID DEFAULT NEWID(), - - StateProvinceName NVARCHAR(100) NOT NULL, - - ISO3166_2 CHAR(6) NOT NULL, - -- eg 'US-CA' for California, 'CA-ON' for Ontario - - CountryID UNIQUEIDENTIFIER NOT NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_StateProvince - PRIMARY KEY (StateProvinceID), - - CONSTRAINT AK_StateProvince_ISO3166_2 - UNIQUE (ISO3166_2), - - CONSTRAINT FK_StateProvince_Country - FOREIGN KEY (CountryID) - REFERENCES Country (CountryID) -); - -CREATE -NONCLUSTERED INDEX IX_StateProvince_Country - ON StateProvince(CountryID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE City -( - CityID UNIQUEIDENTIFIER - CONSTRAINT DF_CityID DEFAULT NEWID(), - - CityName NVARCHAR(100) NOT NULL, - - StateProvinceID UNIQUEIDENTIFIER NOT NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_City - PRIMARY KEY (CityID), - - CONSTRAINT FK_City_StateProvince - FOREIGN KEY (StateProvinceID) - REFERENCES StateProvince (StateProvinceID) -); - -CREATE -NONCLUSTERED INDEX IX_City_StateProvince - ON City(StateProvinceID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BreweryPost -- A user cannot be deleted if they have a post -( - BreweryPostID UNIQUEIDENTIFIER - CONSTRAINT DF_BreweryPostID DEFAULT NEWID(), - - BreweryName NVARCHAR(256) NOT NULL, - - PostedByID UNIQUEIDENTIFIER NOT NULL, - - Description NVARCHAR(MAX) NOT NULL, - - CreatedAt DATETIME NOT NULL - CONSTRAINT DF_BreweryPost_CreatedAt DEFAULT GETDATE(), - - UpdatedAt DATETIME NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_BreweryPost - PRIMARY KEY (BreweryPostID), - - CONSTRAINT FK_BreweryPost_UserAccount - FOREIGN KEY (PostedByID) - REFERENCES UserAccount (UserAccountID) - ON DELETE NO ACTION -); - -CREATE -NONCLUSTERED INDEX IX_BreweryPost_PostedByID - ON BreweryPost(PostedByID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BreweryPostLocation -( - BreweryPostLocationID UNIQUEIDENTIFIER - CONSTRAINT DF_BreweryPostLocationID DEFAULT NEWID(), - - BreweryPostID UNIQUEIDENTIFIER NOT NULL, - - AddressLine1 NVARCHAR(256) NOT NULL, - - AddressLine2 NVARCHAR(256), - - PostalCode NVARCHAR(20) NOT NULL, - - CityID UNIQUEIDENTIFIER NOT NULL, - - Coordinates GEOGRAPHY NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_BreweryPostLocation - PRIMARY KEY (BreweryPostLocationID), - - CONSTRAINT AK_BreweryPostLocation_BreweryPostID - UNIQUE (BreweryPostID), - - CONSTRAINT FK_BreweryPostLocation_BreweryPost - FOREIGN KEY (BreweryPostID) - REFERENCES BreweryPost (BreweryPostID) - ON DELETE CASCADE, - - CONSTRAINT FK_BreweryPostLocation_City - FOREIGN KEY (CityID) - REFERENCES City (CityID) -); - -CREATE -NONCLUSTERED INDEX IX_BreweryPostLocation_BreweryPost - ON BreweryPostLocation(BreweryPostID); - -CREATE -NONCLUSTERED INDEX IX_BreweryPostLocation_City - ON BreweryPostLocation(CityID); - --- To assess when the time comes: - --- This would allow for efficient spatial queries to find breweries within a certain distance of a location, but it adds overhead to insert/update operations. - --- CREATE SPATIAL INDEX SIDX_BreweryPostLocation_Coordinates --- ON BreweryPostLocation(Coordinates) --- USING GEOGRAPHY_GRID --- WITH ( --- GRIDS = (LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM), --- CELLS_PER_OBJECT = 16 --- ); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BreweryPostPhoto -- All photos linked to a post are deleted if the post is deleted -( - BreweryPostPhotoID UNIQUEIDENTIFIER - CONSTRAINT DF_BreweryPostPhotoID DEFAULT NEWID(), - - BreweryPostID UNIQUEIDENTIFIER NOT NULL, - - PhotoID UNIQUEIDENTIFIER NOT NULL, - - LinkedAt DATETIME NOT NULL - CONSTRAINT DF_BreweryPostPhoto_LinkedAt DEFAULT GETDATE(), - - RowVersion ROWVERSION, - - CONSTRAINT PK_BreweryPostPhoto - PRIMARY KEY (BreweryPostPhotoID), - - CONSTRAINT FK_BreweryPostPhoto_BreweryPost - FOREIGN KEY (BreweryPostID) - REFERENCES BreweryPost (BreweryPostID) - ON DELETE CASCADE, - - CONSTRAINT FK_BreweryPostPhoto_Photo - FOREIGN KEY (PhotoID) - REFERENCES Photo (PhotoID) - ON DELETE CASCADE -); - -CREATE -NONCLUSTERED INDEX IX_BreweryPostPhoto_Photo_BreweryPost - ON BreweryPostPhoto(PhotoID, BreweryPostID); - -CREATE -NONCLUSTERED INDEX IX_BreweryPostPhoto_BreweryPost_Photo - ON BreweryPostPhoto(BreweryPostID, PhotoID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BeerStyle -( - BeerStyleID UNIQUEIDENTIFIER - CONSTRAINT DF_BeerStyleID DEFAULT NEWID(), - - StyleName NVARCHAR(100) NOT NULL, - - Description NVARCHAR(MAX), - - RowVersion ROWVERSION, - - CONSTRAINT PK_BeerStyle - PRIMARY KEY (BeerStyleID), - - CONSTRAINT AK_BeerStyle_StyleName - UNIQUE (StyleName) -); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BeerPost -( - BeerPostID UNIQUEIDENTIFIER - CONSTRAINT DF_BeerPostID DEFAULT NEWID(), - - Name NVARCHAR(100) NOT NULL, - - Description NVARCHAR(MAX) NOT NULL, - - ABV DECIMAL(4, 2) NOT NULL, - -- Alcohol By Volume (typically 0-67%) - - IBU INT NOT NULL, - -- International Bitterness Units (typically 0-120) - - PostedByID UNIQUEIDENTIFIER NOT NULL, - - BeerStyleID UNIQUEIDENTIFIER NOT NULL, - - BrewedByID UNIQUEIDENTIFIER NOT NULL, - - CreatedAt DATETIME NOT NULL - CONSTRAINT DF_BeerPost_CreatedAt DEFAULT GETDATE(), - - UpdatedAt DATETIME, - - RowVersion ROWVERSION, - - CONSTRAINT PK_BeerPost - PRIMARY KEY (BeerPostID), - - CONSTRAINT FK_BeerPost_PostedBy - FOREIGN KEY (PostedByID) - REFERENCES UserAccount (UserAccountID) - ON DELETE NO ACTION, - - CONSTRAINT FK_BeerPost_BeerStyle - FOREIGN KEY (BeerStyleID) - REFERENCES BeerStyle (BeerStyleID), - - CONSTRAINT FK_BeerPost_Brewery - FOREIGN KEY (BrewedByID) - REFERENCES BreweryPost (BreweryPostID), - - CONSTRAINT CHK_BeerPost_ABV - CHECK (ABV >= 0 AND ABV <= 67), - - CONSTRAINT CHK_BeerPost_IBU - CHECK (IBU >= 0 AND IBU <= 120) -); - -CREATE -NONCLUSTERED INDEX IX_BeerPost_PostedBy - ON BeerPost(PostedByID); - -CREATE -NONCLUSTERED INDEX IX_BeerPost_BeerStyle - ON BeerPost(BeerStyleID); - -CREATE -NONCLUSTERED INDEX IX_BeerPost_BrewedBy - ON BeerPost(BrewedByID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BeerPostPhoto -- All photos linked to a beer post are deleted if the post is deleted -( - BeerPostPhotoID UNIQUEIDENTIFIER - CONSTRAINT DF_BeerPostPhotoID DEFAULT NEWID(), - - BeerPostID UNIQUEIDENTIFIER NOT NULL, - - PhotoID UNIQUEIDENTIFIER NOT NULL, - - LinkedAt DATETIME NOT NULL - CONSTRAINT DF_BeerPostPhoto_LinkedAt DEFAULT GETDATE(), - - RowVersion ROWVERSION, - - CONSTRAINT PK_BeerPostPhoto - PRIMARY KEY (BeerPostPhotoID), - - CONSTRAINT FK_BeerPostPhoto_BeerPost - FOREIGN KEY (BeerPostID) - REFERENCES BeerPost (BeerPostID) - ON DELETE CASCADE, - - CONSTRAINT FK_BeerPostPhoto_Photo - FOREIGN KEY (PhotoID) - REFERENCES Photo (PhotoID) - ON DELETE CASCADE -); - -CREATE -NONCLUSTERED INDEX IX_BeerPostPhoto_Photo_BeerPost - ON BeerPostPhoto(PhotoID, BeerPostID); - -CREATE -NONCLUSTERED INDEX IX_BeerPostPhoto_BeerPost_Photo - ON BeerPostPhoto(BeerPostID, PhotoID); - ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- - -CREATE TABLE BeerPostComment -( - BeerPostCommentID UNIQUEIDENTIFIER - CONSTRAINT DF_BeerPostComment DEFAULT NEWID(), - - Comment NVARCHAR(250) NOT NULL, - - BeerPostID UNIQUEIDENTIFIER NOT NULL, - - CommentedByID UNIQUEIDENTIFIER NOT NULL, - - Rating INT NOT NULL, - - CreatedAt DATETIME NOT NULL - CONSTRAINT DF_BeerPostComment_CreatedAt DEFAULT GETDATE(), - - UpdatedAt DATETIME NULL, - - RowVersion ROWVERSION, - - CONSTRAINT PK_BeerPostComment - PRIMARY KEY (BeerPostCommentID), - - CONSTRAINT FK_BeerPostComment_BeerPost - FOREIGN KEY (BeerPostID) - REFERENCES BeerPost (BeerPostID), - - CONSTRAINT FK_BeerPostComment_UserAccount - FOREIGN KEY (CommentedByID) - REFERENCES UserAccount (UserAccountID) - ON DELETE NO ACTION, - - CONSTRAINT CHK_BeerPostComment_Rating - CHECK (Rating BETWEEN 1 AND 5) -); - -CREATE -NONCLUSTERED INDEX IX_BeerPostComment_BeerPost - ON BeerPostComment(BeerPostID); - -CREATE -NONCLUSTERED INDEX IX_BeerPostComment_CommentedBy - ON BeerPostComment(CommentedByID);