Proper Database Design with Foreign Keys - database

Because I've never designed a database, I wanted to make sure that the design I'm using, while simple, follows general idiomatic design patterns.
Essentially, a friend is making a discord bot that allows you to submit photos and have others rate them. Putting the obvious trolling opportunities aside, here are the data fields that are needed:
Discord ID (unique ID for every person using discord)
List of URLs (each URL is tied to a certain discord member)
List of Votes (each vote has a score, the voter ID, and the URL)
What I don't particularly like about this design is that it maintains two scores: a running total that will be divided by the total number of votes of that user, and each vote in particular.
My questions are:
Is this design proper?
Using this design, how can I ensure that each person can only vote for each url once?
https://dbdesigner.net output:
CREATE TABLE "Members" (
"id" serial NOT NULL,
"discord_id" bigint NOT NULL,
"total_score" bigint NOT NULL,
"total_votes" bigint NOT NULL,
CONSTRAINT Members_pk PRIMARY KEY ("id")
) WITH (
OIDS=FALSE
);
CREATE TABLE "Images" (
"id" serial NOT NULL,
"url" TEXT(64) NOT NULL,
"member_id" bigint NOT NULL,
CONSTRAINT Images_pk PRIMARY KEY ("id")
) WITH (
OIDS=FALSE
);
CREATE TABLE "Votes" (
"id" serial NOT NULL,
"voter_id" serial NOT NULL,
"target_id" serial NOT NULL,
"score" serial NOT NULL,
"image_id" serial NOT NULL,
CONSTRAINT Votes_pk PRIMARY KEY ("id")
) WITH (
OIDS=FALSE
);
ALTER TABLE "Images" ADD CONSTRAINT "Images_fk0" FOREIGN KEY ("member_id") REFERENCES "Members"("discord_id");
ALTER TABLE "Votes" ADD CONSTRAINT "Votes_fk0" FOREIGN KEY ("voter_id") REFERENCES "Members"("discord_id");
ALTER TABLE "Votes" ADD CONSTRAINT "Votes_fk1" FOREIGN KEY ("target_id") REFERENCES "Members"("discord_id");
ALTER TABLE "Votes" ADD CONSTRAINT "Votes_fk2" FOREIGN KEY ("image_id") REFERENCES "Images"("id");

Since I cannot see your foreign key references and I don't see your code (i.e., SQL statements), I cannot know for sure if your synthetic keys are a good idea. But at first glance, it appears as though your real key for VOTES is (VOTER_ID, IMAGE_URL).
If we assume that you are not going to change the relations, their keys, and their non-key attributes, then all you need to do to satisfy #2 is to put a unique constraint on VOTES (VOTER_ID, IMAGE_URL).

Responding to the first part of the question, "Is this design proper", the short answer is "no".
If discord_ids are unique, you do not need another ID column in members. The discord_id is the primary key of the members table.
If Image URLs are unique, that could be the primary key of the Images table. That's really up to you; some people don't like using long text strings as keys. I'll assume you're one of them.
The Votes table shouldn't have an ID column at all. It's a many-to-many join table. Your key there is (voter_id, image_id). This also has the effect of preventing members from voting more than once.
The target_id column in votes is completely redundant, as that information already exists in the images table.
Neither voter_id nor image_id in Votes should be Serial. Instead, they should be INT. Score, which is presumably a numeric score, should be NUMERIC or INT (I'll use INT since total_score is bigint).
using mixed-case identifiers is generally a bad idea in SQL, as identifier (table) names are case-sensitive in strange ways.
Limiting URLs to 64 characters seems shortsighted; do you have an application constraint here you need to match?
You should add CASCADE to all of your foriegn keys, so that you can easily delete members or images.
As such, below is your revised schema:
CREATE TABLE "members" (
"discord_id" bigint NOT NULL,
"total_score" bigint NOT NULL,
"total_votes" bigint NOT NULL,
CONSTRAINT members_pk PRIMARY KEY ("discord_id")
);
CREATE TABLE "images" (
"id" serial NOT NULL,
"url" VARCHAR(64) NOT NULL,
"discord_id" BIGINT NOT NULL,
CONSTRAINT images_pk PRIMARY KEY ("id"),
CONSTRAINT images_url UNIQUE ("url")
);
CREATE TABLE "votes" (
"voter_id" INT NOT NULL,
"image_id" INT NOT NULL,
"score" INT NOT NULL,
CONSTRAINT votes_pk PRIMARY KEY (voter_id, image_id)
);
ALTER TABLE "images" ADD CONSTRAINT "images_fk0"
FOREIGN KEY ("discord_id") REFERENCES "members"("discord_id")
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "votes" ADD CONSTRAINT "votes_fk0"
FOREIGN KEY ("voter_id") REFERENCES "members"("discord_id")
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "votes" ADD CONSTRAINT "votes_fk2"
FOREIGN KEY ("image_id") REFERENCES "images"("id")
ON DELETE CASCADE ON UPDATE CASCADE;

Related

How to add Foreign key constraint on array in PostgreSQL?

How to add Foreign key constraint on array in PostgreSQL?
look_up table for roles
CREATE TABLE party_role_cd
(
party_role_cd bigint NOT NULL,
code character varying(80) NOT NULL,
CONSTRAINT party_role_cd PRIMARY KEY (party_role_cd)
);
Party can have zero or many roles [ 0-N relationship ]
CREATE TABLE party
(
party_id biging NOT NULL,
party_role_cd bigint[] NOT NULL,
CONSTRAINT party_id PRIMARY KEY (party_id)
);
How to add the foreign key constraint for party_role_cd array in party table?
That's not implemented in PostgreSQL. Currently, FK constraints only operate on equality between whole column values. No array-to-element references. There is an open TODO item that has been added in 2010. See:
https://wiki.postgresql.org/wiki/Todo#Referential_Integrity
https://www.postgresql.org/message-id/1288033876.6278.6.camel#vanquo.pezone.net
There were even attempts to implement it, but never to completion.

Shortcut to Assign Multiple Foreign Keys Using Keyword CONSTRAINT

Currently, I have the following code in my SQLQuery:
CREATE TABLE OrderTable (
OrderNumber varchar(3) NOT NULL,
Date_Time datetime NOT NULL,
WaitID varchar(7) NOT NULL,
CashID varchar(7),
TableNumber varchar(3) NOT NULL,
CONSTRAINT PK_Order PRIMARY KEY (OrderNumber, Date_Time),
CONSTRAINT FK_WaitID FOREIGN KEY (WaitID) REFERENCES WaiterWaitress(ID),
CONSTRAINT FK_CashID FOREIGN KEY (CashID) REFERENCES Cashier(ID),
CONSTRAINT FK_TableNumber FOREIGN KEY (TableNumber) REFERENCES RestTable(TableNumber)
);
I had to retype the CONSTRAINT ... FOREIGN KEY ... REFERENCES ... three times. Is there a shortcut to this while still maintaining the ability to name the foreign key (i.e. FK_WaitID)?
You could do some sort of complicated script after you run the CREATE TABLE statement to add the constraints without repeating the actual keyword "CONSTRAINT", but that code will not "look a bit better".
Writing it this way keeps it clear for others in the future who may need to review the structure. The constraints and keys are clearly laid out, and that's really the way it should be.

Entity Framework appears to skip mapping a table with a multi-column primary key set through a constraint

I've created an ADO.NET model via the database first approach.
One of my tables which is listed when creating the model doesn't actually get added to it.
The table has a multi-column primary key, composed of two foreign keys.
CREATE TABLE ForumAccess
(
UserID INT FOREIGN KEY REFERENCES Users(UserID) NOT NULL,
ForumID INT FOREIGN KEY REFERENCES Forums(ForumID) NOT NULL,
CONSTRAINT ForumAccessID PRIMARY KEY (UserID, ForumID),
);
It does show up when I have to select which tables to add, but then it seems to be skipped. No class is generated for it, and it's not shown in the .edmx file.
Part of my application depends on the existence of this table. I have another table which has a multi-column primary key, and another DateTime type column. That table does get added.
That table is:
CREATE TABLE Moderators
(
UserID INT FOREIGN KEY REFERENCES Users(UserID) NOT NULL,
ForumID INT FOREIGN KEY REFERENCES Forums(ForumID) NOT NULL,
TimeOfAddition DateTime NOT NULL, -- When the mod was added as a mod.
CONSTRAINT ModeratorID PRIMARY KEY (UserID, ForumID),
);
Why does the Moderators table get added, but the ForumAccess table doesn't?
There is no error, or any warning that I can see.
What am I missing?

SQL Server foreign relationship on two-column primary key

I have the following three sample tables (simplified demo for purpose of question):
CREATE TABLE Teams
(
Id int IDENTITY(1,1) NOT NULL,
Name varchar(50) NOT NULL,
PRIMARY KEY (Id)
)
CREATE TABLE TeamGroups
(
Id int NOT NULL,
TeamId int NOT NULL,
PRIMARY KEY (Id,TeamId)
)
CREATE TABLE RoomBookings
(
Id int NOT NULL,
TeamGroupId int NOT NULL,
RoomId int NOT NULL,
PRIMARY KEY (Id)
)
and I have the following foreign key already set up:
ALTER TABLE TeamGroups WITH CHECK
ADD CONSTRAINT [FK_TeamGroups_Teams]
FOREIGN KEY (TeamId) REFERENCES Teams(Id)
The idea is that each Team can be in zero or more TeamGroups, and each TeamGroup can have zero or more RoomBookings
To reflect that, I want to add a foreign key from the RoomBookings table into the TeamGroups table.
I tried using the Relationships GUI in Management Studio to create the foreign key (primary key table: TeamGroups.ID, foreign key table: RoomBookings.TeamGroupId) but I get an error:
The columns in table 'TeamGroups' do not match an existing primary key
or UNIQUE constraint
I'm assuming it's because the TeamGroups table has a two-column primary key?
I don't really want to make a foreign key constraint from the TeamGroups table (eg, the key is present in the TeamGroups table), as the table will eventually be used by other tables (such as EquipmentBookings, GroupManagers, etc).
Any help?
If your primary key is made up from more than one columns, then all foregin keys also must have all those columns - there's no way around this.
But I don't understand why you'd get this error trying to link TeamGroups to Team based on the Team.Id column.... that should work just fine.
Try using this:
ALTER TABLE TeamGroups WITH CHECK
ADD CONSTRAINT [FK_TeamGroups_Teams]
FOREIGN KEY (TeamId) REFERENCES Teams(Id);
You had Teams (which is not a valid column in TeamGroups at all), and you had REFERENCES Teams.id which is wrong - it needs to be REFERNCES Teams(Id); (column in parenthesis - not the "dot" notation)
Update: from TeamGroups to RoomBookings - yes.... either use both columns from TeamGroups in your RoomBookings table - or what would stop you from making the TeamGroups.Id column an INT IDENTITY and then have the PK on just this one column?? Any good reason for that??
CREATE TABLE TeamGroups
(
TeamGroupId int NOT NULL,
TeamId int NOT NULL,
PRIMARY KEY (TeamGroupId)
)
ALTER TABLE dbo.RoomBookings
ADD CONSTRAINT FK_RoomBookings_TeamGroup
FOREIGN KEY TeamGroupId REFERENCES TeamGroups(TeamGroupId)

Primary and Foreign Key at the same time

Would it be possible in SQL Server 2008 to have a table created with 2 columns that are at the same time primary and foreign keys? If yes, how would such a code look like? I've searched and came up with nothing.
Sure, no problem:
CREATE TABLE dbo.[User]
(
Id int NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(1024) NOT NULL
);
CREATE TABLE [Group]
(
Id int NOT NULL IDENTITY PRIMARY KEY,
Name nvarchar(1024) NOT NULL
);
CREATE TABLE [UserToGroup]
(
UserId int NOT NULL,
GroupId int NOT NULL,
PRIMARY KEY CLUSTERED ( UserId, GroupId ),
FOREIGN KEY ( UserId ) REFERENCES [User] ( Id ) ON UPDATE NO ACTION ON DELETE CASCADE,
FOREIGN KEY ( GroupId ) REFERENCES [Group] ( Id ) ON UPDATE NO ACTION ON DELETE CASCADE
);
This is quite commonly used to model many-to-many relations.
These are totally different constructs.
A Primary Key is used to enforce uniqueness within a table, and be a unique identifier for a certain record.
A Foreign Key is used for referential integrity, to make sure that a value exists in another table.
The Foreign key needs to reference the primary key in another table.
If you want to have a foreign key that is also unique, you could make a FK constraint and add a unique index/constraint to that same field.
For reference purposes, SQL Server allows a FK to refer to a UNIQUE CONSTRAINT as well as to a PRIMARY KEY field.
It is probably not a good idea since often you want to allow duplicate foreign keys in the table. Even if you don't now, in the future, you might, so best not to do this. See Is it fine to have foreign key as primary key?
Just a quick note - from Microsoft pages (http://msdn.microsoft.com/en-us/library/ms189049.aspx)...
"A foreign key constraint does not have to be linked only to a primary key constraint in another table; it can also be defined to reference the columns of a UNIQUE constraint in another table."
Not used often, but useful in some circumstances.

Resources