I'm doing an exercise to learn database designs. This is some kind of fantasy soccer teams. I have this initial tables, users and teams. One team can have many users and a user can join and belongs to one team.
USERS
PK id
FK team_id -> TEAMS.id
username
TEAMS
PK id
name
But I also want to allow users to create their own teams if they don't want to join another team. What if I added owner_id in teams table?
TEAMS
PK id
FK owner_id -> USERS.id
name
I feel like this is not the right way to do. What is the best way to do this?
You could have a flag in users that indicates if the user owns the team they're a member of and a partial unique index to restrict this flag only being set once for a team.
CREATE TABLE teams
(id serial,
owner_id integer,
PRIMARY KEY (id));
CREATE TABLE users
(id serial,
team_id integer
NOT NULL,
is_owner boolean
NOT NULL
DEFAULT FALSE,
PRIMARY KEY (id),
FOREIGN KEY (team_id)
REFERENCES teams
(id));
CREATE UNIQUE INDEX users_team_id_is_owner
ON users
(team_id)
WHERE is_owner;
db<>fiddle
The index only includes the row that satisfy the WHERE clause, thus all of them have the flag is_owner set to true. Within that subset of rows the team_id must be unique which follows that there can not be any two rows with the same team_id and is_owner set to true.
To ensure a user must be a member of a team, set the team_id to be NOT NULL.
There can be teams without owners in that schema though. But as I understood your question, that is OK.
Related
If i have three type of users. Let's say seller, consumers, and sales persons. Should i make individual table for there details like name, email passwords and all other credentials etc with a role_type table or separate table for each of them. Which is the best approach for a large project considering all engineering principles for DBMS like normalization etc.
Also tell me Does it effect the performance of the app if i have lots of joins in tables to perform certain operations?
If the only thing that distinguishes those people is the role but all details are the same, then I would definitely go for a single table.
The question is however, can a single person have more than one role? If that is never the case, then add a role_type column to the person table. Depending on how fixed those roles are maybe use a lookup table and a foreign key, e.g.:
create table role_type
(
id integer primary key,
name varchar(20) not null unique
);
create table person
(
id integer primary key,
.... other attributes ...,
role_id integer not null references role_type
);
However, in my experience the restriction to exactly one role per person usually doesn't hold, so you would need a many-to-many relation ship
create table role_type
(
id integer primary key,
name varchar(20) not null unique
);
create table person
(
id integer primary key,
.... other attributes ...,
);
create table person_role
(
person_id integer not null references person,
role_id integer not null references role_type,
primary key (person_id, role_id)
);
It sounds like this is a case of trying to model inheritance in your relational database. Complex topic, discussed here and here.
It sounds like your "seller, consumer, sales person" will need lots of different attributes and relationships. A seller typically belongs to a department, has targets, is linked to sales. A consumer has purchase history, maybe a credit limit, etc.
If that's the case,I'd suggest "class table inheritance" might be the right solution.
That might look something like this.
create table user_account
(id int not null,
username varchar not null,
password varchar not null
....);
create table buyer
(id int not null,
user_account_id int not null(fk),
credit_limit float not null,
....);
create table seller
(id int not null,
user_account_id int not null(fk),
sales_target float,
....);
To answer your other question - relational databases are optimized for joining tables. Decades of research and development have gone into this area, and a well-designed database (with indexes on the columns you're joining on) will show no noticeable performance impact due to joins. From practical experience, queries with hundreds of millions of records and ten or more joins run very fast on modern hardware.
I want to ensure that one worker has exactly one manager
CREATE TABLE IF NOT EXISTS user_relationships (
object_id SERIAL PRIMARY KEY NOT NULL UNIQUE,
manager_id INT REFERENCES users (object_id) NOT NULL,
worker_id INT REFERENCES users (object_id) NOT NULL,
CHECK (manager_id != worker_id),
UNIQUE (manager_id, worker_id)
);
I have series of SQL statements using Read Committed level of transaction isolation,
BEGIN
SELECT * FROM users WHERE id=manager_id AND acc_type="manager" FOR UPDATE;
SELECT * FROM users WHERE id=worker_id AND acc_type="worker" FOR UPDATE;
SELECT * FROM relationships WHERE id=worker;
INSERT INTO relationships (m, w) VALUES (manager_id, worker_id)
COMMIT
I have figured out the first two FOR UPDATE to prevent other
concurrent transactions from changing the users account type mid
transaction
I could not figure out what kind of "trick" to use for third query. Third query should return empty list to ensure that the worker has not yet been owned by any manager.
Third query FOR UPDATE does not work because I am expecting an empty row.
Due to third query, I run the risk of concurrent transaction adding duplicate worker to different managers.
What can I do to enforce one worker to one manager?
Probably you need to add UNIQUE to constraint to worker_id:
CREATE TABLE IF NOT EXISTS user_relationships (
object_id SERIAL PRIMARY KEY NOT NULL UNIQUE,
manager_id INT REFERENCES users (object_id) NOT NULL,
worker_id INT REFERENCES users (object_id) UNIQUE NOT NULL,
CHECK (manager_id != worker_id)
);
But is better to add field manager_id INT REFERENCES users (object_id) NOT NULL to table users and do not use user_relationships.
I'm using a PostgreSQL 9.3 Database to create a small Userdatabase. One user can be a part of different groups.
There are 2 Tables in the Database:
user and group
The data type of all columns is text
Every row in user is a single user, every row in group is a single group.One column in user should be groups
How may i configure the groups-column to add multiple entries of group to a single user
What you are describing is a standard many-to-many relationship in a relational database.
You solve this problem by creating a mapping table between users and groups:
If you have something like this currently:
create table users (id integer primary key, user_name text);
create table groups (id integer primary key, group_name text);
The mapping table would look something like this:
create table user_groups
(
user_id not null references users,
group_id not null references groups,
primary key (user_id, group_id)
);
By using (user_id, group_id) as the primary key, you make sure that a user can only be mapped to the same group once.
I am thinking of three tables;
Employee with emp_id an, emp_name and other related data.
and
Person table with person_id, person_name and other related data.
and
GroupList with grp_list_id, member_id (either emp_id or person_id) and is_employee (true for employee and false for person) and other related data.
This is the first time I am trying to do a table whose foreign key can come from two different tables. Can someone please suggest the best way to achieve it?
So, i'm currently working with a database system where a user can register, login and update his/her details whenever.
The database includes 5 roles:
1. Public
2. Member
3. Moderator
4. Coordinator
5. Admin
I want to be able to assign multiple roles to my users. For example, the Admin can also be a member. Therefore in the database it should show:
User_id | Role_ID
------------------------
user1 | 2, 5
^ is it possible to add multivalued id's in postgresql?
You can use filed of type array to store list of values.
However I think that there is much better way to organize what you want.
Make one table: role_names and another roles, like that:
CREATE TABLE role_names
(
id serial NOT NULL,
name text NOT NULL,
CONSTRAINT role_names_pkey PRIMARY KEY (id),
CONSTRAINT role_names_name_key UNIQUE (name)
);
CREATE TABLE roles
(
user_id bigint NOT NULL,
role_id bigint NOT NULL,
CONSTRAINT roles_pkey PRIMARY KEY (user_id, role_id),
CONSTRAINT roles_role_id_fkey FOREIGN KEY (role_id)
REFERENCES role_names (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT
);
Now in the table role_names you put all the roles that you want to have.
In the table roles, you can assign or delete any number of roles to any user.
Also you can search in table roles for specific users or specific roles - much neat and faster than searching into arrays I think.
Feel free to add FK constraint for the user_id field too.
Yes, you can use int array to store list of roles.
Here's related question -Junction tables vs foreign key arrays?