SQL One-to-Many Table vs. multiple one-to-one relationships - sql

I'm working on a project with the following objective: A User can create a Challenge and select an optional Rival to take part of this challenge. The Challenge generates Daily entries and will track stats on these.
The basic User and Entry entities look like this:
CREATE TABLE users (
id (INT),
PRIMARY KEY (id)
);
CREATE TABLE entries (
challengeId INT,
userId INT,
entryDate DATE,
entryData VARCHAR,
PRIMARY KEY (challengeId, userId, entryDate)
)
The piece I'm having trouble with is the Challenge piece with the Rival concept. I can see two approaches.
// Hard code the concept of a Challenge Owner and Rival:
CREATE TABLE challenges (
id INT,
name VARCHAR,
ownerId INT,
rivalId INT NULL,
PRIMARY KEY (id),
UNIQUE KEY (ownerId, name)
);
// Create Many-to-one relationship.
CREATE TABLE challenges (
id INT,
name VARCHAR,
PRIMARY KEY (id),
UNIQUE KEY (name)
)
CREATE TABLE participant (
challengeId INT,
userId INT,
isOwner BIT,
PRIMARY KEY (challengeId, userId)
)
The problem with the first approach is that referential integrity is tough since now there are two columns where userIds reside (ownerId and rivalId). I'd have to create two tables for everything (owner_entries, rival_entries, owner_stats, etc.) in order to set up foreign keys.
The second approach solves this and has some advantages like allowing multiple rivals in the future. However, one thing I can't do anymore with that approach is enforce Challenge name uniqueness across a single user instead of the whole Challenge table. Additionally, tasks like finding a Challenge's owner is now trickier.
What's the right approach to the Challenges table? Is there anyway to set up these tables in a developer friendly manner or should I just jump all the way to Class Table Inheritance and manage the concept of Owner/Rivals there?

I think the way I would set this up is as follows (using the second approach):
CREATE TABLE challenges (id INT,
name VARCHAR,
owner_id INT,
PRIMARY KEY (id),
UNIQUE KEY (name, owner_id))
CREATE TABLE participant (challengeId INT,
userId INT,
PRIMARY KEY (challengeId, userId))
This allows easy tracking of who owns the challenge, yet extracts out the individual participants.
This would also allow you to unique the challenge name by the owner safely, and foreign keys on the userId in participant are easy. 'Rivals' are then all participants that are not the challenge owner.

I treat the first approach the right one.
You could have one table for users and one for challenges.
Are you aware that you can reference one table twice like below?
SELECT * FROM CHALLENGES
INNER JOIN USERS AS OWNERS ON OWNERS.ID = CHALLENGES.OWNERID
INNER JOIN USERS AS RIVALS ON RIVALS.ID = CHALLENGES.RIVALID
In this case you can reference both rivals and owners without creating new tables.

Related

Foreign key to another table

I'm creating a database for storing user data and repeated data for each user (for example if it was for running it might have distance, time, date, etc. for each run). The users are all in one table
CREATE TABLE users ( name varchar(30), id int primary key );
Each user will submit data that needs to be put into a different table (schema doesn't matter here). I could either make this set of data into one big table wiht all the submitted data plus a key to the user
CREATE TABLE data ( ..., user_id int REFERENCES users );
or my prefered way which would be have one table per user and have each entry in the users table somehow refence the whole table for that user. I'm not quite sure how you'd do this and can't find any way to do so so far.
Any help is very appreciated thanks.
It's ok to use one table for all users data, just add index on field user_id:
CREATE TABLE users ( name varchar(30), id int primary key );
CREATE TABLE data (
--- ...,
user_id int REFERENCES users (id)
);
CREATE INDEX data_user_id_ix ON data(user_id);
https://sqlize.online/sql/psql11/44a7c7e8145db1e0583f8c91025da72f/

Creating foreign key in Visual Studio 2010

I'm trying to create 2 tables in Visual Studio server pane and I have a problem creating the foreign keys.
When I'm trying to create them in the relationship page it won't let me create foreign keys until I make them primary keys.
I don't understand why I can't have a different member in my table that it is primary key and a different one a foreign key.
My tables are like this:
Table People:
Primary key: ID int
Name nvarchar
Age int
City nvarchar
Foreign key: AccountNumber int --> when I make him primary key everything works.
Table BankAccount:
Primary key and Foreign Key: AccountNumber int
Money float
ps.
I used some mysql with CMD and I remember that I could do this, but because I don't have this control in visual studio I'm lost.
UPDATE:
Rup made a good point that i only relize afther answering the qustion he ask me
i just did the realtionship (Foreign Key) for the bank account table and now i got
what i wanted thank you!
Why is BankAccount.AccountNumber a Foreign Key? Are you trying to reference back to the the person who owns it?
If that is the case, BankAccount should be:
CREATE TABLE BankAccount
(
AccountNumber int PK,
Money float, // bad idea. Use currency or int (i.e. store balance as pennies)
Owner int FK REFERENCES People.ID
)
And drop the account ID from the people table.
This models the relationship "an Account has 1 and only one owner" and allows the relationship "A person can have many accounts."
It looks like what you're trying to do is create a back reference. If you were to choose to do this, then the back reference to the owner should be on People.ID, not on account. -- Although this is unnecessary because you can simply query
SELECT * FROM
BankAccount INNER JOIN People ON BankAccount.Owner = People.ID
to get all accounts and their owners.
If you want to establish multiple owners for an account, AND many accounts per user, then you need to establish a third table to implement the many-to-many relationship.
CREATE TABLE AccountOwners
(
AccOwnID int PK,
AccountID int FK REFERENCES BankAccount.AccountNumber,
PeopleID int FK REFERENCES People.ID
)
And drop the People.AccountNumber and BankAccount.Owner. This technique is akin to maintaining a list of all accounts and owners which you can query.

Need help understanding this SQL (generated by doctrine)

i am actually reading Doctrine Reference: One to Many, Unidirectional with Join table. but this will probably be more of a SQL quesiton. basically, this is supposed to model a one to many, unidirectional relationship. i guess from the PHP code (in that link), its such that 1 user have many phonenumbers.
the question is from the SQL, it seems like 1 user can have many phonenumbers. and 1 phonenumber can only belong to 1 user. am i right?
CREATE TABLE User (
id INT AUTO_INCREMENT NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
CREATE TABLE users_phonenumbers (
user_id INT NOT NULL,
phonenumber_id INT NOT NULL,
UNIQUE INDEX users_phonenumbers_phonenumber_id_uniq (phonenumber_id),
PRIMARY KEY(user_id,
phonenumber_id)
) ENGINE = InnoDB;
CREATE TABLE Phonenumber (
id INT AUTO_INCREMENT NOT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id);
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id);
can't i just simplify the database to ... below ... no need for join tables and what not?
Users (id, name)
Phonenumbers (id, user [FK], number)
Correct, these are two valid approaches to the same problem. And yes, the unique index on users_phonenumbers means that each phone number can belong to only one user.
The design is actually suboptimal.
The idea must have been that there are telephone numbers, users, and that they can be linked many-to-many. Because of the unique index on phonenumberid hoever, each number can only be assigned to one user.
Then the whole users_phonenumbers has become redundant, because they could just have added a userid column on the phonenumbers table and save themselves a join.
BAd table design if you ask me.

implementing UNIQUE across linked tables in MySQL

a USER is a PERSON and a PERSON has a COMPANY - user -> person is one-to-one, person -> company is many-to-one.
person_id is FK in USER table.
company_id is FK in PERSON table.
A PERSON may not be a USER, but a USER is always a PERSON.
If company_id was in user table, I could create a unique key based on username and company_id, but it isn't, and would be a duplication of data if it was.
Currently, I'm implementing the unique username/company ID rule in the RoseDB manager wrapper code, but it feels wrong. I'd like to define the unique rule at the DB level if I can, but I'm not sure excactly how to approach it. I tried something like this:
alter table user add unique(used_id,person.company_id);
but that doesn't work.
By reading through the documentation, I can't find an example that does anything even remotely similar. Am I trying to add functionality that doesn't exist, or am I missing something here?
Well, there's nothing simple that does what you want. You can probably enforce the constraint you need using BEFORE INSERT and BEFORE UPDATE triggers, though. See this SO question about raising MySQL errors for how to handle making the triggers fail.
Are there more attributes to your PERSON table? Reason I ask is that what you want to implement is a typical corollary table:
USERS table:
user_id (pk)
USER_COMPANY_XREF (nee PERSON) table:
user_id (pk, fk)
company_id (pk, fk)
EFFECTIVE_DATE (not null)
EXPIRY_DATE (not null)
COMPANIES table:
company_id (pk)
The primary key of the USER_COMPANY_XREF table being a composite key of USERS.user_id and COMPANIES.company_id would allow you to associate a user with more than one company while not duplicating data in the USERS table, and provide referencial integrity.
You could define the UNIQUE constraint in the Person table:
CREATE TABLE Company (
company_id SERIAL PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE Person (
person_id SERIAL PRIMARY KEY,
company_id BIGINT UNSIGNED,
UNIQUE KEY (person_id, company_id),
FOREIGN KEY (company_id) REFERENCES Company (company_id)
) ENGINE=InnoDB;
CREATE TABLE User (
person_id BIGINT UNSIGNED PRIMARY KEY,
FOREIGN KEY (person_id) REFERENCES Person (person_id)
) ENGINE=InnoDB;
But actually you don't need the unique constraint even in the Person table, because person_id is already unique on its own. There's no way a given person_id could reference two companies.
So I'm not sure what problem you're trying to solve.
Re your comment:
That doesn't solve the issue of allowing the same username to exist in different companies.
So you want a given username to be unique within one company, but usable in different companies? That was not clear to me from your original question.
So if you don't have many other attributes specific to users, I'd combine User with Person and add an "is_user" column. Or just rely on it being implicitly true that a Person with a non-null cryptpass is by definition a User.
Then your problem with cross-table UNIQUE constraints goes away.

Polymorphism in SQL database tables?

I currently have multiple tables in my database which consist of the same 'basic fields' like:
name character varying(100),
description text,
url character varying(255)
But I have multiple specializations of that basic table, which is for example that tv_series has the fields season, episode, airing, while the movies table has release_date, budget etc.
Now at first this is not a problem, but I want to create a second table, called linkgroups with a Foreign Key to these specialized tables. That means I would somehow have to normalize it within itself.
One way of solving this I have heard of is to normalize it with a key-value-pair-table, but I do not like that idea since it is kind of a 'database-within-a-database' scheme, I do not have a way to require certain keys/fields nor require a special type, and it would be a huge pain to fetch and order the data later.
So I am looking for a way now to 'share' a Primary Key between multiple tables or even better: a way to normalize it by having a general table and multiple specialized tables.
Right, the problem is you want only one object of one sub-type to reference any given row of the parent class. Starting from the example given by #Jay S, try this:
create table media_types (
media_type int primary key,
media_name varchar(20)
);
insert into media_types (media_type, media_name) values
(2, 'TV series'),
(3, 'movie');
create table media (
media_id int not null,
media_type not null,
name varchar(100),
description text,
url varchar(255),
primary key (media_id),
unique key (media_id, media_type),
foreign key (media_type)
references media_types (media_type)
);
create table tv_series (
media_id int primary key,
media_type int check (media_type = 2),
season int,
episode int,
airing date,
foreign key (media_id, media_type)
references media (media_id, media_type)
);
create table movies (
media_id int primary key,
media_type int check (media_type = 3),
release_date date,
budget numeric(9,2),
foreign key (media_id, media_type)
references media (media_id, media_type)
);
This is an example of the disjoint subtypes mentioned by #mike g.
Re comments by #Countably Infinite and #Peter:
INSERT to two tables would require two insert statements. But that's also true in SQL any time you have child tables. It's an ordinary thing to do.
UPDATE may require two statements, but some brands of RDBMS support multi-table UPDATE with JOIN syntax, so you can do it in one statement.
When querying data, you can do it simply by querying the media table if you only need information about the common columns:
SELECT name, url FROM media WHERE media_id = ?
If you know you are querying a movie, you can get movie-specific information with a single join:
SELECT m.name, v.release_date
FROM media AS m
INNER JOIN movies AS v USING (media_id)
WHERE m.media_id = ?
If you want information for a given media entry, and you don't know what type it is, you'd have to join to all your subtype tables, knowing that only one such subtype table will match:
SELECT m.name, t.episode, v.release_date
FROM media AS m
LEFT OUTER JOIN tv_series AS t USING (media_id)
LEFT OUTER JOIN movies AS v USING (media_id)
WHERE m.media_id = ?
If the given media is a movie,then all columns in t.* will be NULL.
Consider using a main basic data table with tables extending off of it with specialized information.
Ex.
basic_data
id int,
name character varying(100),
description text,
url character varying(255)
tv_series
id int,
BDID int, --foreign key to basic_data
season,
episode
airing
movies
id int,
BDID int, --foreign key to basic_data
release_data
budget
What you are looking for is called 'disjoint subtypes' in the relational world. They are not supported in sql at the language level, but can be more or less implemented on top of sql.
You could create one table with the main fields plus a uid then extension tables with the same uid for each specific case. To query these like separate tables you could create views.
Using the disjoint subtype approach suggested by Bill Karwin, how would you do INSERTs and UPDATEs without having to do it in two steps?
Getting data, I can introduce a View that joins and selects based on specific media_type but AFAIK I cant update or insert into that view because it affects multiple tables (I am talking MS SQL Server here). Can this be done without doing two operations - and without a stored procedure, natually.
Thanks
Question is quite old but for modern postresql versions it's also worth considering using json/jsonb/hstore type.
For example:
create table some_table (
name character varying(100),
description text,
url character varying(255),
additional_data json
);