mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Merge branch 'main' into frontend/brewery-feature
This commit is contained in:
@@ -498,12 +498,16 @@ For the table-by-table schema and the custom SQL error code scheme, see
|
|||||||
```
|
```
|
||||||
scripts/
|
scripts/
|
||||||
└── 01-schema/
|
└── 01-schema/
|
||||||
└── schema.sql # full table/index/constraint definitions
|
├── 01-UserAccount.sql
|
||||||
|
├── 02-Photo.sql
|
||||||
|
├── ...
|
||||||
|
└── 16-BeerPostComment.sql # one script per CREATE TABLE
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently the whole schema is a single versioned script. As the schema evolves,
|
Each table has its own script, numbered so a table's foreign keys always point
|
||||||
new numbered scripts get added alongside it rather than editing `schema.sql` in
|
at an earlier-numbered script. As the schema evolves, new numbered scripts get
|
||||||
place, so DbUp's already-applied tracking stays valid.
|
added rather than editing an existing one in place, so DbUp's already-applied
|
||||||
|
tracking stays valid.
|
||||||
|
|
||||||
### Data seeding
|
### Data seeding
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ convention. For the vertical-slice code that talks to this schema, see
|
|||||||
|
|
||||||
## Schema
|
## Schema
|
||||||
|
|
||||||
The full schema lives in one script:
|
The full schema lives under
|
||||||
`web/backend/Database/Database.Migrations/scripts/01-schema/schema.sql`, run by
|
`web/backend/Database/Database.Migrations/scripts/01-schema/`, one numbered
|
||||||
|
script per table (`01-UserAccount.sql`, `02-Photo.sql`, ...), run in order by
|
||||||
`Database.Migrations` (DbUp) on startup. Each table is queried directly by the
|
`Database.Migrations` (DbUp) on startup. Each table is queried directly by the
|
||||||
owning feature slice's repository (see
|
owning feature slice's repository (see
|
||||||
[Direct SQL via Dapper/ADO.NET](../architecture.md#direct-sql-via-dapperadonet)).
|
[Direct SQL via Dapper/ADO.NET](../architecture.md#direct-sql-via-dapperadonet)).
|
||||||
|
|
||||||
|
A generated DBML copy of the schema (for import into a diagramming tool such
|
||||||
|
as dbdiagram.io) lives at
|
||||||
|
`web/backend/Database/Database.Migrations/schema.dbml`.
|
||||||
|
|
||||||
All tables use a `UNIQUEIDENTIFIER` primary key defaulting to `NEWID()`, and
|
All tables use a `UNIQUEIDENTIFIER` primary key defaulting to `NEWID()`, and
|
||||||
most carry a `RowVersion ROWVERSION` column used for optimistic concurrency on
|
most carry a `RowVersion ROWVERSION` column used for optimistic concurrency on
|
||||||
updates (`UPDATE ... WHERE ... AND RowVersion = @RowVersion`).
|
updates (`UPDATE ... WHERE ... AND RowVersion = @RowVersion`).
|
||||||
@@ -50,10 +55,11 @@ repository or seed data yet:
|
|||||||
**Tool**: DbUp, run by the `Database.Migrations` project/container.
|
**Tool**: DbUp, run by the `Database.Migrations` project/container.
|
||||||
|
|
||||||
Scripts live under `scripts/`, applied in order and tracked so a script never
|
Scripts live under `scripts/`, applied in order and tracked so a script never
|
||||||
re-runs once it has succeeded. Today that's a single `01-schema/schema.sql`.
|
re-runs once it has succeeded. Today that's `01-schema/`, with one script per
|
||||||
When the schema needs to change, add a new numbered script rather than editing
|
table, numbered so a table's foreign keys always point at an earlier-numbered
|
||||||
`schema.sql` in place — DbUp's tracking is per-script, so editing an
|
script. When the schema needs to change, add a new numbered script rather than
|
||||||
already-applied script means it won't re-run against existing databases.
|
editing an existing one in place — DbUp's tracking is per-script, so editing
|
||||||
|
an already-applied script means it won't re-run against existing databases.
|
||||||
|
|
||||||
## Data seeding
|
## Data seeding
|
||||||
|
|
||||||
|
|||||||
238
web/backend/Database/Database.Migrations/schema.dbml
Normal file
238
web/backend/Database/Database.Migrations/schema.dbml
Normal file
@@ -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]
|
||||||
@@ -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)
|
||||||
|
);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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)
|
||||||
|
);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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
|
||||||
|
-- );
|
||||||
@@ -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);
|
||||||
@@ -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)
|
||||||
|
);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
|
||||||
Reference in New Issue
Block a user