Database table that has many-to-many and one-to-many relationship - mysql

In the interest of learning more about database design im was drawing up a database model, i choose to draw a simple database model for a social network website to keep a little more interesting than your average student/teacher/class models.
The question i have is about the relationships between the different tables.
Im not that good drawing these text database drawings like other peoples has on stack exchange ill try to just list the tables and explain the relationships, if its unclear i can try to draw a text drawing.
Database tables:
User
Friend
Group
Newsfeed
User has a one-to-many relationship to Friend and Group based on that one user can have many friends and a user can be a member of several groups and a group can have many users.
Friend has a many-to-many relationship with Group based on that one friend can be a part of many groups and a group can contain many friends. There is a one-to-many relationship to Newsfeed based on that one friend can have many newsfeeds.
Group has a many-to-many relationship with Friend based on that one group can contain many friends and one friend can be part of many groups. Group has a one-to-many relationships with Newsletter based on that one group can have many newsfeeds.
So now there is one many-to-many relationship and a one-to-many relationship in one table point to two other tables, is this correct ? Some part of this feels wrong, especially the Friend part but maybe im just misunderstanding something here. This might be a stupid database model but i need to ask stupid questions sometimes in order to get smarter at something. Ive read about and watch some videos about database relationships and to be they seem easy but when drawing this database model im getting confused since i suddently end up with a many-to-many and a one-to-many relationship in one table which seems weird.

This is how I'd start:
Let's say we have two groups, Group A and Group B.
groups
id unsigned int(P)
name varchar(30)
...
+----+---------+-----+
| id | name | ... |
+----+---------+-----+
| 1 | Group A | ... |
| 2 | Group B | ... |
| .. | ....... | ... |
+----+---------+-----+
Let's say Group A has two newsfeeds and Group B doesn't have any:
newsfeeds
id unsigned int(P)
group_id unsigned int(F groups.id)
name varchar(30)
...
+----+----------+--------------------+-----+
| id | group_id | name | ... |
+----+----------+--------------------+-----+
| 1 | 1 | Interesting Things | ... |
| 2 | 1 | Other Information | ... |
| .. | ........ | .................. | ... |
+----+----------+--------------------+-----+
Let's say we have three users: Bob, Mary and John:
users
id unsigned int(P)
name varchar(30)
...
+----+------+-----+
| id | name | ... |
+----+------+-----+
| 1 | Bob | ... |
| 2 | Mary | ... |
| 3 | John | ... |
| .. | .... | ... |
+----+------+-----+
A "Friend" is really just another user so let's create a table that allows many-to-many relationships between two users. My sample data shows that Bob is friends with Mary and John while Mary is only friends with John. (user_id and friend_id form the Primary Key)
users_friends
user_id unsigned int \_ (P) (F users.id)
friend_id unsigned int / (F users.id)
+---------+-----------+
| user_id | friend_id |
+---------+-----------+
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| ....... | ......... |
+---------+-----------+
Users can belong to many groups and each group can have many users so we need to have a table that gives us that many-to-many relationship. In my example data we see that Bob is a member of Group A and Group B while Mary and John are only members of Group B. (user_id and group_id form the Primary Key)
users_groups
user_id unsigned int \_ (P)(F users.id)
group_id unsigned int / (F groups.id)
+---------+----------+
| user_id | group_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| ....... | ........ |
+---------+----------+
Finally we need a table that shows the relationship between newsfeeds and users. I haven't entered any example data here but this table works exactly like the users_groups table. Tables like this are called many different things, you can read more about them at Wikipedia. (user_id and newsfeed_id form the Primary key)
users_newsfeeds
user_id unsigned int \_ (P) (F users.id)
newsfeed_id unsigned int / (F newsfeeds.id)

IMO, when thinking about relation modeling, one should remember about the 'direction' of the relation, otherwise it gets very confusing and also, one should remember every 'many to many' relation must have to be modeled using 'one to many'. Anyway, take a look here http://screencast.com/t/sJbPrvO53MS
even though it took a min to read the question...this was interesting problem...

Related

Two foreign keys reference one table and null able foreign key

I am new to Database tables and relationships .I need some help for the below requirements
Work flow
1. Hospital will have Male Patient
2. Hospital will have Female Patient
3. Hospital Will have Couple Patient but in RegTable it will stored as separate record for male and female.
For the above requirements i have designed the table structure below
Approach 1
RegTable
+-------+---------+---------+
| RegID | Name | Gender |
+-------+---------+---------+
| 1 | XXX | M |
| 2 | XXX | M |
| 3 | Husband | M |
| 4 | Wife | F |
+-------+---------+---------+
RegDetail
+----+------+-------+
| Id | FK_1 | FK_2 |
+----+------+-------+
| 1 | 1 | Null |
| 2 | 2 | Null |
| 3 | 3 | 4 |
+----+------+-------+
FK_1,FK_2 is RegId from Regtable
I have two questions
Is my current approach is correct or Not ?
Is alternative approach is there for the above work flow .
Kindly help me solve this . Thanks in Advance
You don't need 2 tables here.You can do it as shown below.
RegTables - this is the only table you need
Id int PK
Name string
Gender String
PatientType tinyint
Here you can maintain enum Type for separating Single and couple.
public enum PatientType : byte
{
Single=1,
Couple =2,
}
Update :
Treatments table
Id int PK
Name string
RegId int FK --> this is the foreign key referencing RegTables table
I would suggest the third table RegRecords with field
id, note, date. It will contain a registration data without link to RegTable. So you will store links to real people in RegDetail that will have only two fields: FK_KEY_RegRecords and FK_KEY_ RegTable.

How to keep staff and users in separate tables with another table that gives them a general accountID?

I have two tables, staff and users. Below are shortened versions of the tables.
(* = PK, # = FK)
staff { staffID*, username, ... }
users { userID*, username, ... }
I want to make it so that these tables have a table that links them together in a way that they are given an accountID.
I was thinking that this will be something along the lines of...
accounts { accountID*, accountType, localID# }
...where localID is either; the staffID if the account is from the staff table, or the userID if the account is from the users table. The accountType would be the used to tell which table the account is from and would have possible values of staff and user.
First off - is this possible to accomplish this way? Is this a good idea? Since both the staff and users tables have the field username, should I instead use username in the accounts table in place of accountType and localID? (If there exists a record in the staff table with the username 'foo', then there cannot be a record in the user table with the username 'foo')
Secondly - If the tables above are the way that I should implement this, what constraint would I need to add to make it so accountType & localID are unique such that all the examples in Figure 1 would be accepted, whereas the examples in Figure 2 that are duplicate combinations of accountType and localID would not?
Figure 1
|-----------|-------------|---------|
| accountID | accountType | localID |
|-----------|-------------|---------|
| 1 | A | 1 |
| 2 | B | 1 |
| 3 | A | 2 |
| 4 | B | 2 |
|-----------|-------------|---------|
Figure 2
|-----------|-------------|---------|
| accountID | accountType | localID |
|-----------|-------------|---------|
| 1 | A | 1 |
| 2 | A | 1 |
| 3 | B | 2 |
| 4 | B | 2 |
|-----------|-------------|---------|
Reasoning - I want these separate tables for staff and users to because I want role based access control, but I want it such that there are roles only a staff account can have & there are other roles that only a user account can have.
Thanks in advance for any help/guidance offered.
Add the accountId in staff and user table, that would accomplish what your looking for.

One-to-one relation through pivot table

Okay so I have a soccer website im building when a user signs up they get a team and and 6 different stadium to chose from. so I have a teams table:
----------------------------------------
| user_id | team_name | stadium_id |
----------------------------------------
| 1 | barca | 2 |
----------------------------------------
Then I decided to make the stadiums its own table
----------------------------------------------
| id | name | capacity | price |
----------------------------------------------
| 1 | Maracana | 50000 | 90000 |
------------------------------------------------
| 2 | Morombi | 80000 | 150000 |
------------------------------------------------
to get the teams arena name I would have to get the arena_id from the teams table and then fetch the arena name with the id. Now I don't think this is very efficient so I gave it some thought and I think the best solution is adding a pivot table like so:
| id | arena_id | team_id |
---------------------- ----------------
| 1 | 2 | 1
--------------------------------------|
| 2 | 1 | 2
--------------------------------------|
I usually think of pivot tables as tables for many to many relationships not one to one relationships. Is using a pivot table in this instance the best solution or should I just leave the implementation I'm currently using?
You don't need to use a pivot-table for this. It can either be a One-To-One or a One-To-Many relationship. It's a One-To-One if every user/team does only relate to one stadium (no stadium can be used by two teams). In a One-To-Many relationship multiple teams/users could use the same stadium, which might become necessary if you have thousands of users and start running out of stadiums.
A JOIN statement would be efficient and sufficient here.
SELECT s.name, t.team_name -- Get the team's and stadium's name
FROM team t -- From the team table
JOIN stadium s -- Join it with the stadium table
ON (t.stadium_id = s.id) -- Join ON the stadium_id
This will return the team name and stadium name of every registered team.
You might need to adjust the query, but you should be able to catch the grasp of it after reading the MySQL reference I linked above.

How To Design A Database for a "Check In" Social Service

I want to build a "check in" service like FourSquare or Untappd.
How do I design a suitable database schema for storing check-ins?
For example, suppose I'm developing "CheeseSquare" to help people keep track of the delicious cheeses they've tried.
The table for the items into which one can check in is fairly simple and would look like
+----+---------+---------+-------------+--------+
| ID | Name | Country | Style | Colour |
+----+---------+---------+-------------+--------+
| 1 | Brie | France | Soft | White |
| 2 | Cheddar | UK | Traditional | Yellow |
+----+---------+---------+-------------+--------+
I would also have a table for the users, say
+-----+------+---------------+----------------+
| ID | Name | Twitter Token | Facebook Token |
+-----+------+---------------+----------------+
| 345 | Anne | qwerty | poiuyt |
| 678 | Bob | asdfg | mnbvc |
+-----+------+---------------+----------------+
What's the best way of recording that a user has checked in to a particular cheese?
For example, I want to record how many French cheeses Anne has checked-in. Which cheeses Bob has checked into etc. If Cersei has eaten Camembert more than 5 times etc.
Am I best putting this information in the user's table? E.g.
+-----+------+------+--------+------+------+---------+---------+
| ID | Name | Blue | Yellow | Soft | Brie | Cheddar | Stilton |
+-----+------+------+--------+------+------+---------+---------+
| 345 | Anne | 1 | 0 | 2 | 1 | 0 | 5 |
| 678 | Bob | 3 | 1 | 1 | 1 | 1 | 2 |
+-----+------+------+--------+------+------+---------+---------+
That looks rather ungainly and hard to maintain. So should I have separate tables for recordings check in?
No, don't put it into the users table. That information is better stored in a join table which represents a many-to-many relationship between users and cheeses.
The join table (we'll call cheeses_users) must have at least two columns (user_ID, cheese_ID), but a third (a timestamp) would be useful too. If you default the timestamp column to CURRENT_TIMESTAMP, you need only insert the user_ID, cheese_ID into the table to log a checkin.
cheeses (ID) ⇒ (cheese_ID) cheeses_users (user_ID) ⇐ users (ID)
Created as:
CREATE TABLE cheeses_users
cheese_ID INT NOT NULL,
user_ID INT NOT NULL,
-- timestamp defaults to current time
checkin_time DATETIME DEFAULT CURRENT_TIMESTAMP,
-- (add any other column *specific to* this checkin (user+cheese+time))
--The primary key is the combination of all 3
-- It becomes impossible for the same user to log the same cheese
-- at the same second in time...
PRIMARY KEY (cheese_ID, user_ID, checkin_time),
-- FOREIGN KEYs to your other tables
FOREIGN KEY (cheese_ID) REFERENCES cheeses (ID),
FOREIGN KEY (user_ID) REFERENCES users (ID),
) ENGINE=InnoDB; -- InnoDB is necessary for the FK's to be honored and useful
To log a checkin for Bob & Cheddar, insert with:
INSERT INTO cheeses_users (cheese_ID, user_ID) VALUES (2, 678);
To query them, you join through this table. For example, to see the number of each cheese type for each user, you might use:
SELECT
u.Name AS username,
c.Name AS cheesename,
COUNT(*) AS num_checkins
FROM
users u
JOIN cheeses_users cu ON u.ID = cu.user_ID
JOIN cheeses c ON cu.cheese_ID = c.ID
GROUP BY
u.Name,
c.Name
To get the 5 most recent checkins for a given user, something like:
SELECT
c.Name AS cheesename,
cu.checkin_time
FROM
cheeses_users cu
JOIN cheeses c ON cu.cheese_ID = c.ID
WHERE
-- Limit to Anne's checkins...
cu.user_ID = 345
ORDER BY checkin_time DESC
LIMIT 5
Let's define more clearly, so you can tell me if I'm wrong:
Cheese instances exist and aren't divisible ("Cheddar/UK/Traditional/Yellow" is a valid checkinable cheese, but "Cheddar" isn't, nor is "Yellow" or "Cheddar/France/...)
Users check into a single cheese instance at a given time
Users can re-check into the same cheese instance at a later date.
If this is the case, then to store fully normalized data, and to be able to retrieve that data's history, you need a third relational table linking the two existing tables.
+-----+------------+---------------------+
| uid | cheese_id | timestamp |
+----+-------------+---------------------+
| 345 | 1 | 2014-05-04 19:04:38 |
| 345 | 2 | 2014-05-08 19:04:38 |
| 678 | 1 | 2014-05-09 19:04:38 |
+-----+------------+---------------------+
etc. You can add extra columns to correspond to the cheese data, but strictly speaking you don't need to.
By putting all this in a third table, you potentially improve both performance and flexibility. You can always reconstruct the additions to the users table you mooted, using aggregate queries.
If you really decide you don't need the timestamps, then you'd replace them with basically the equivalent of a COUNT(*) field:
+-----+------------+--------------+
| uid | cheese_id | num_checkins |
+----+-------------+--------------+
| 345 | 1 | 15 |
| 345 | 2 | 3 |
| 678 | 1 | 8 |
+-----+------------+--------------+
That would dramatically reduce the size of your joining table, although obviously there's less of a "paper trail", should you need to reconstruct your data (and possibly say to a user "oh, yeah, we forgot to record your checkin on such-a-date.")
The entities 'User' and 'Cheese' have a many-to-many relationship. A user can have multiple cheeses he checked into, and a cheese can have multiple people that checked into it.
The only right way to design this in a relational database is to store it into a separate table. There are many reasons why storing it into the user table for instance, is a very bad idea. Read up on normalizing databases for more info on this.
Your table should look something like this:
CheckIns(CheeseId, UserId, (etc...))
Other useful columns might include date or rating, or whatever you want to store about a particular relationship between a user and a cheese.

One to many table with 3M records

I have a table in MySQL that contains almsot 3 million records.
The table saves friend information in a user system. So it has many users and even more friends (There is a (soft)max of 2000 per user). I had added some extra fields name, url, dob, image, registered which are varchar(255) and dates.
My basic data is 2 int's and 1 varchar(6).
When using PHPMyAdmin it all gets really slow. I have an index on the user ID and the varchar(6) and that's how I query all the friends of a user (which goes well). However, any other operation (or the ones to come) aren't going to be fast.
My options:
Remove the double data (Normalizing)
Change the datatype for the friend IDs and save it like a JSON blob
So questions;
When my table is only 2 ints and a tiny varchar, will it still be
slow with 3M records?
Should I change my datatype?
Should I be
using a different pattern for this friendlist problem?
Edit: To clarify a bit more.
The Users are not my actual users, but they are user objects nonetheless. All the Friends are a User object, but I may or may not already have the User object. So I'm using the extra data in Friends to show data about it in the list on the Users page.
In the ideal world things wouldn't take so long, in the next optimal world I would only have 2 fields in Friends which are user_id and friend_id. But I can not rely on linking friend_id to a User object, I may not have it..
Users (has more fields, but for brevity)
+-------+---------+-------+------------+
| shard | user_id | name | dob |
+-------+---------+-------+------------+
| nl | 1 | Bob | 2014-03-26 |
| nl | 2 | Erik | 2014-03-26 |
| de | 1 | Johan | 2014-02-01 |
+-------+---------+-------+------------+
Friends (has more fields, see description above)
+-------+---------+-----------+--------+
| shard | user_id | friend_id | name |
+-------+---------+-----------+--------+
| nl | 1 | 2 | Erik |
| nl | 1 | 3 | Alice |
| de | 1 | 2 | Rasmus |
+-------+---------+-----------+--------+
nl-Bob is friends with nl-Erik (Is a user)
nl-Bob is friends with nl-Alice (Is not a user)
de-Johan is friends with de-Rasmus (Is not a user)