3

I'm experimenting by making a social network from scratch in PHP/MySQL, but I'm having trouble thinking of the optimal MySQL structure for it, currently I have:

This is a table which stores all user info:

fname varchar (300),
sname varchar (300),
pass varchar (400),
email varchar (300),
gender varchar (300),
dob varchar (200),
uid varchar (300),
PRIMARY KEY (id)

This is created when a user signs up, their own personal table:

id int(20) NOT NULL auto_increment,
            uid varchar (300),
            photo_url varchar (400),
            pfid varchar (300),
            phototime datetime,
            video_url varchar (400),
            vfid varchar (300),
            videotime datetime,
            status longtext,
            sid varchar (300),
            statustime datetime,
            blog longtext,
            bid varchar (300),
            blogtime datetime,
            about_bio longtext,
            about_current_job longtext,
            about_secondary_school longtext,
            about_primary_school longtext,
            about_college longtext,
            about_university longtext,
            about_workemail longtext,
            about_homeemail longtext,
            about_phonenumber longtext,
            about_relationshipstatus longtext,
            about_relationshipwith longtext,
            PRIMARY KEY (id)
            )";

The sessions table to track whether someone is logged in or not:

id int(20) NOT NULL auto_increment,
sid varchar(300),
uid varchar(300),
PRIMARY KEY (id)

Haven't gotten onto relationships yet but I was thinking:

id int(20) NOT NULL auto_increment,
requestby varchar(200),
requestto varchar(200),
status varchar(200)

(Before anyone asks, this is purely just for the learning experience, nothing more)

dbc
  • 104,963
  • 20
  • 228
  • 340
AviateX14
  • 760
  • 4
  • 18
  • 36
  • There's a few problems... first, don't create a new table for every user, that would be unmanageable. Instead, add records into existing tables when a new user is added. Next, you need to tighten up your data types. Don't store everything in varchars, and don't make the varchar longer than what is needed to store the value, such as `gender varchar (300)`, `dob varchar (200)`, etc... – Michael Fredrickson Mar 29 '12 at 18:12
  • That's just my general feedback, but did you have a specific question? – Michael Fredrickson Mar 29 '12 at 18:13
  • Good point. There is no question here :S – Mosty Mostacho Mar 29 '12 at 18:33

2 Answers2

5

Well, you definitely shouldn't have one table per user. I think a database structure more like this would work really well:

CREATE TABLE users (
    userID INT NOT NULL AUTO_INCREMENT,
    firstName VARCHAR(30),
    lastName VARCHAR(30),
    password CHAR(32), -- should be encrypted, CHAR is better if the field is always the same length
    email VARCHAR(64) NOT NULL, -- not null if this is what you will use as a "username"
    PRIMARY KEY (userID)
);

CREATE TABLE personalInfo (
    userID INT NOT NULL,
    gender ENUM ('MALE', 'FEMALE'),
    dateOfBirth DATE,
    phoneNumber VARCHAR(15),
    personalEmail VARCHAR(64), -- may or may not be the same as the email field in the "users" table
    workEmail VARCHAR(64),
    bio TEXT,
    FOREIGN KEY (userID) REFERENCES users (userID)
);

/* this table is not specific to any single user. It is just a list of jobs that have been created */
CREATE TABLE jobs (
    jobID INT NOT NULL AUTO_INCREMENT,
    company VARCHAR(100),
    title VARCHAR(100),
    description TEXT,
    PRIMARY KEY (jobID)
);

/* the workInfo table will hold one entry per user per job. So if a user has held five jobs,
   there will be five rows with that userID in this table, each with a different jobID, which
   refers to an entry in the "jobs" table above. */
CREATE TABLE workInfo (
    userID INT NOT NULL,
    jobID INT NOT NULL,
    startDate DATE,
    endDate DATE, -- can set this to null if it's the user's current job
    FOREIGN KEY (userID) REFERENCES users (userID),
    FOREIGN KEY (jobID) REFERENCES jobs (jobID)
);

CREATE TABLE schools (
    schoolID INT NOT NULL AUTO_INCREMENT,
    schoolName VARCHAR(100),
    -- any other information you want to provide about the school (city, address, phone, etc)
    PRIMARY KEY (schoolID)
);

CREATE TABLE schoolPrograms (
    programID INT NOT NULL AUTO_INCREMENT,
    programName VARCHAR(100),
    -- any other information you want to provide about the program (department, teachers, etc)
    PRIMARY KEY (programID)
);

CREATE TABLE educationInfo (
    userID INT NOT NULL,
    schoolID INT,
    programID INT,
    startDate DATE,
    endDate DATE,
    FOREIGN KEY (userID) REFERENCES users (userID),
    FOREIGN KEY (schoolID) REFERENCES schools (schoolID),
    FOREIGN KEY (programID) REFERENCES schoolPrograms (programID)
);

CREATE TABLE relationships (
    userID INT NOT NULL,
    userID2 INT, -- allowed to be null if the user is single or does not specify who they are in a relationship with
    status ENUM ('SINGLE', 'IN A RELATIONSHIP', 'MARRIED', 'IT''S COMPLICATED' /* etc */),
    FOREIGN KEY (userID) REFERENCES users (userID)
);

/* each photo is created here. This way, when a user wants to share a photo,
   we don't have to duplicate each column. We just create another row in
   the "userPhotos" table below that) REFERENCES the same photoID. */
CREATE TABLE photos (
    photoID INT NOT NULL AUTO_INCREMENT,
    url VARCHAR(200),
    caption VARCHAR(200),
    dateOfUpload TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (photoID)
);

CREATE TABLE userPhotos (
    userID INT NOT NULL,
    photoID INT NOT NULL,
    FOREIGN KEY (userID) REFERENCES users (userID),
    FOREIGN KEY (photoID) REFERENCES photos (photoID)
);

/* vidoes, handled exactly the same as photos */
CREATE TABLE videos (
    videoID INT NOT NULL AUTO_INCREMENT,
    url VARCHAR(200),
    caption VARCHAR(200),
    dateOfUpload TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (videoID)
);

CREATE TABLE userVideos (
    userID INT NOT NULL,
    videoID INT NOT NULL,
    FOREIGN KEY (userID) REFERENCES users (userID),
    FOREIGN KEY (videoID) REFERENCES videos (videoID)
);

CREATE TABLE status (
    userID INT NOT NULL,
    status TEXT,
    FOREIGN KEY (userID) REFERENCES users (userID)
);
Travesty3
  • 14,351
  • 6
  • 61
  • 98
  • Nice, but to start with, I would choose to have a little less profile information, or you'll be programming for a couple of weeks to only write a sign-up page. :) (If you're a beginner) – GolezTrol Mar 29 '12 at 18:54
  • Okay, and what about for statuses? – AviateX14 Mar 29 '12 at 18:59
  • @AviateX14: See updated answer. Hopefully you get the idea and can expand on this for any new tables you would want to create. – Travesty3 Mar 29 '12 at 19:03
  • @GolezTrol: Doesn't hurt to have your database structure planned out ahead of time. You don't have to utilize all the tables right away. You can just start using them when you're ready. Which is another very good reason to use relational tables, so you can easily expand to add more features without always having to alter your tables, which can sometimes cause complications. – Travesty3 Mar 29 '12 at 19:06
  • @Travesty3 something I don't quite get, say a user uploads a photo, how does the database link the user to that photo? The UserID right? So would I have to insert the UserID into the table with each upload/status/video? – AviateX14 Mar 29 '12 at 19:39
  • @AviateX14: Every time the user uploads a photo, you create a row in the `photos` table that holds the photo information AND you create a row in the `userPhotos` table that links the user to the photo. You would do the same for videos. For status updates, you might just insert a row into the status table each time the user updates his status. Might be a good idea to have a `DATETIME` column in the `status` table as well, so you can keep them in order. – Travesty3 Mar 29 '12 at 19:44
  • @AviateX14: On the other hand, if another user sees the photo/video on one person's profile and wants to share it on their own profile as well, you can just create a row in the `userPhotos` or `userVideos` table that links that user to the photo/video. You don't need to create another row in the `photos` or `videos` tables because they are referencing a photo or video that already exists in that table. – Travesty3 Mar 29 '12 at 19:46
  • Okay, final question, then I'll set you free ;P When I try to create the tables, I get this error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'REFERENCES users (userID) )' at line 9 – AviateX14 Mar 29 '12 at 19:59
  • See updated answer. My fault on the syntax errors. I tested them this time. – Travesty3 Mar 29 '12 at 23:15
  • @Travesty3: You just came up with that schema? – Alix Axel Mar 29 '12 at 23:22
  • 1
    @AlixAxel: Yessir. I found it enlightening to think of how I would design some of Facebook's tables. – Travesty3 Mar 29 '12 at 23:25
  • And If I need to show all post of a user, I need to join how many tables :/ – Anand Singh Jan 30 '17 at 13:55
  • @AnandSingh: With a [normalized](https://en.wikipedia.org/wiki/Database_normalization) database, you will join tables, but with proper foreign keys and indexing, I don't see a problem with that at all. As a disclaimer, I wrote this answer about five years ago, when I had plenty more time to spend coming up with very detailed answers. I don't have much time these days, so I haven't even really reviewed my old answer to understand and see how many joins would be needed, or how to improve the answer. Feel free to add your own answer with a better schema. – Travesty3 Jan 30 '17 at 17:00
0

Don't use large varchars for all those fields. Friendship status can be just an int if you keep a lookup table (or a list in your code) that explains each value.

If the user table has an auto incrementing ID, you could use that ID for foreign key relationships. Even if you don't want UID to be an integer, you could still make it a GUID or something else that is much, much smaller than a varchar.

These tables only specify a profile and maybe a relationship, but there is so much more. Even something as simple as Twitter has a table of tweets, lists, accounts to put in a list, users that follow a list, direct messages (although those could theoretically be in the same table as Tweets), linked apps, blocked users and much, much more.

So I think first of all, you should think about what your social network should be, what it should look like, what features should it have. Then, strip that down to only the most essential features. Then, strip it down a little more, you're still thinking too big. ;) When you got clear what your minimum desirement are, it will probably be much clearer to you what table you would need.

Don't forget to add constraints and indexes!

Note that in practice, Twitter, Facebook and the other large networks don't use MySQL at all, but to practice, MySQL is fine.

GolezTrol
  • 114,394
  • 18
  • 182
  • 210
  • I thought fb uses mysql as per http://gigaom.com/cloud/facebook-shares-some-secrets-on-making-mysql-scale/ ? – Tarang Mar 29 '12 at 19:06
  • Apparently. Didn't know that. I also assumed FaceBook would be using a noSQL solution. Thanks for pointing this out! – GolezTrol Mar 29 '12 at 19:21
  • @GolezTrol Facebook uses both MySql and NoSql (Not Only Sql). [MySql for persistence, Memcached for caching between MySql and application logic.](http://www.quora.com/What-is-Facebooks-architecture) – Michael Fredrickson Mar 29 '12 at 20:06