From what I've been reading online, I understood that it's better to split the data into more tables, if possible because of the access times.
Right now I have a table in which I am storing usernames, passwords and join date
This is how my table looks:
'user'
'user_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
user_username VARCHAR(80) NOT NULL,
user_password VARCHAR(255) NOT NULL,
user_join_date INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (user_id) ');
I am trying to create a new table called profiles in which I want to store first name, last name, email, age and gender. What I think is that I need a one-to-one relationship, so I should be using the user_id but I'm not really sure how to implement it.
Should I create another table called profiles with
profiles
profiles_id
first_name
last_name
email
age
gender
and another one which should be the relationship ? e.g.
user_profiles
----------
user_id
profiles_id
Is this right? How would the SQL look for the user_profiles?
Thanks
Don't split the tables. Just add the new columns to your existing user table. You might find later on that splitting tables is a good idea based on actual queries and usage patterns but until you have that kind of data, keep things simple.
If you must create a profile table, don't create a user_profiles table. That would allow an m-to-n relationship which is probably not what you want. A simple user_id column in profiles is better. In fact, it could be both a foreign key and the primary key to make sure that each user row only have one and only one profile row (although by splitting the tables you might still have a user with no profile).
Usually, you create an association table, like user_profiles you have described when one user could have more than one profile, and/or one profile could belong to one or more user.
As you have said, here you have a one-to-one relationship between user and profile. So, you can simply add a user_id column to your profile table, and define it as a foreign key to user table.
Then, a simple JOIN will allow you to query both tables at the same time:
SELECT u.*, p.*
FROM user u
JOIN profile p ON u.user_id = p.user_id
Add a new field in the User table, ProfileId, and set it as Foreign Key (FK). Each time you create a User, you have to assign to it a profile (which will be the ProfileId PK from profile table).
If you want to see also the profile information of a user, you have to do a join
Select username, first_name,second_name
From user u, profile p
Where u.profileId = p.profileId
this
user_profiles
----------
user_id
profiles_id
is used in a many-to-many relationship. By example, you want to assign to an admin some privileges, but those privileges can be also assigned to more admins. Then, you have to create a 3rd table to solve this problem. Here is an example, but you don't need to do this.
You could add a user_id field to your profiles table and JOIN the tables on user_id.
SELECT user.user_username, ..., profiles.first_name, ...
FROM user
INNER JOIN profiles
ON user.user_id = profiles.user_id
This should fetch data combining information from those rows where the JOIN condition is met (i.e. user.user_id = profiles.user_id).
It is true that having more than one tables is a good idea. I am not sure what you mean about access time, but there are other advantages.
- Your users database containing passwords etc is "sacred", you never change its structure and you limit the rights to it (read, write) to the strict minimum.
- You can then have several "satelites" tables such as profiles, private messages, etc which are more flexible, less sensitive and which you can change all the time.
About your question per se, there is no need for a separate table with the relationships. In fact is a very bad idea which will complicate your queries and doesn't have any advantage. Instead, in your profiles database you will have one column that refers back to the user id.
users
--------
id
user_name
email
password
users_profiles
---------
id
user_id
favourite_animal
Table user
user_id |user_username |user_password |user_join_date |profile_id
Table profile
profile_id |first name |last name |email |age |gender
When selecting a user by user id:
SELECT u.*, p.* FROM user AS u INNER JOIN `profile` AS p ON u.profile_id = p.profile_id WHERE u.user_id = 1
But a user should only one gender, one age, one name and surname. Maybe e-mail adresses might be many. I suggest you there is no need to join tables which have a 1-to-1 relation. Instead merge those tables.
Related
I am currently making an android app which uses a feed to display statuses made by users. I have three tables within the same database, each has username either as a primary or unique key column, but each table has different information relating to that user.
For instance, the first table ===>> tbl_users:
username
fname (first name)
mname (middle name)
lname (last name)
etc. (the list is long)
The second table ===>> tbl_userprofilepictures:
profilepictureID
username
profilepicturepath
The third table ===>> tbl_user_feed:
postID (the status' unique ID)
username
status
imagepostpath (the path to the image uploaded with the status)
timestamp
I want to be able to search for the username across all three tables and display the relevant information relating to them on their post. For example I will need their name and surname for tbl_users and I will need their profilepicturepath for tbl_userprofilepictures as well as their status, imagepostpath and timestamp from tbl_user_feed.
Would I need to do this in a seperate PHP file or in the app itself? PS I'm fairly noob at PHP so please feel free to help a bro out.
May the force be with you.
You can use JOIN.
What is JOIN ?
An SQL JOIN clause is used to combine rows from two or more tables, based on a common field between them.
Source : w3schools.com Joins
Here is the sample I made base on your tables given. For these one our common field is username.
SELECT CONCAT('a.fname', 'a.lname'), b.profilepictureID, c.status, c.imagepostpath, c.timestamp
FROM
tbl_users as a
LEFT JOIN tbl_userprofilepictures as b ON b.username = a.username
LEFT JOIN tbl_user_feed as c ON c.username = a.username
Using Alias (table_name as custom_name) is a good practice in joining the tables
I am creating a site that allows users to view desired 'teams' and can then join them with the click of one button.
I have my users table which contains: user_id, user_name, team_id
Then, I have my teams table which contains: team_id, team_name, team_players
How would I go about having the users to join a group, each user can also only be in 1 team at a time.
If you want each user to be able to join multiple teams, and each team to have multiple users, then you need a "join table."
Table teams_users would contain team_id, user_id. You can make a composite primary key on team_id, user_id (preventing a user from joining the same team twice).
Then you can get a team with:
SELECT * FROM users t1 right join teams_users t2 ON t1.team_id = t2.team_id WHERE t2.team_name = 'the rascals'
Even if you only want players to join one team at a time, you might still want to use the join table in case you ever change your mind. It would be very easy. To only allow one team per user, put a unique constraint on user_id in the join table. If you later decide you want to allow multiple teams, you just remove that constraint.
If a user tries the "join team" action, you simply check for the user_id's existence in the join table.
SELECT * FROM teams_users WHERE user_id = $user_id
If it does exist, you retrieve its matching team_id and tell them, "sorry, you are already in team 'the rascals'. You must leave that team if you want to join another." If they drop their team, you simply do:
DELETE from teams_users WHERE user_id = 5
If they add a team, you just do:
INSERT INTO teams_users ($team_id, $user_id) #// (assuming PHP variables).
The INSERT query will only work if they are not already in a team. If they are you would get an error message. You could also look at "INSERT ON DUPLICATE KEY UPDATE ..." queries. But I would advise against that because you want to warn users before they change teams.
You should start by adding the team_id field to the users table as a foreign key and allow it to be NULL.
Then you would display the team names in an html form with a radio button for each team.
In a PHP file (which should be set to the action of your form) create an if statement based on the values you assigned to each radio button. In each if block, execute a sql UPDATE statement that will add the appropriate group_ID to the right user instance.
I have a mySQL table entitled users. It has a UID, rating, username, password, etcetera.
My goal is to make a system (tribes) similar to Facebook's friends list. Each user will be able to view the profile of users and add them to their tribe. Each user is the chief of only one tribe, but can be the villager of as many tribes as he/she wants to be. The rating system would take into account all of the tribe's members ratings.
After doing some research on relational database tables and grouping, I am not clear about how I should go about setting up the tables, or the PHP code that would go along with that.
If someone can get me pointed in the right direction, it'd be much appreciated!
EDIT: One of the big problems I foresee is accepting to be in a tribe. I'm not sure how you account for this.
similar to Facebook's friends list. Each user will be able to view the profile of users and add them to their tribe. Each user is the chief of only one tribe
Okay, so you have a User table, and also will need a Tribe table.
Then the relations you're describing are a chief-of, which is one-to-one (one user can be chief of one tribe; one tribe has only one chief), therefore you can either store this within User (chief_of: Tribe) or within Tribe (chief: User).
CREATE TABLE User ...
chief_of integer
Here, chief_of might be a foreign key so that if you delete a tribe, the relevant tuple will have its chief_of set to NULL (a user can't be chief of a no longer existing tribe).
The membership is a bit more complicated because one user can belong to several tribes, and a tribe will have more than one member.
This is a many-to-many relationship and is usually done with a table holding key pairs:
CREATE TABLE member_of (
user_id integer,
tribe_id integer
);
Both fields are natural candidates for foreign keys. Here you can find a similar implementation using Authors and Books.
To indicate that Bob is a member of the Clan of the Cave Bear, you retrieve the ids of Bob and Bears, and insert a tuple in member_of.
To retrieve all members of the clan, you can use a JOIN:
SELECT Users.* FROM Users
JOIN member_of ON (Users.user_id = member_of.user_id)
WHERE member_of.tribe_id =
(SELECT tribe_id FROM Tribes WHERE tribe_name = 'Clan of the Cave Bear');
I think that a shorter version of that ON in MySQL is USING(user_id) (meaning that both tables have an identical column identically named), but in my opinion the ON is clearer.
You can also retrieve a virtual "is_chief" column:
SELECT Users.*, chief_of = tribe_id AS is_chief FROM Users
JOIN member_of ON (Users.user_id = member_of.user_id)
WHERE member_of.tribe_id =
(SELECT tribe_id FROM Tribes WHERE tribe_name = 'Clan of the Cave Bear');
The one user whose chief_of attribute is equal to the tribe id will have is_chief set to TRUE, which is equal to 1, so
SELECT Users.*, chief_of = tribe_id AS is_chief FROM Users
JOIN member_of ON (Users.user_id = member_of.user_id)
WHERE member_of.tribe_id =
(SELECT tribe_id FROM Tribes WHERE tribe_name = 'Clan of the Cave Bear')
ORDER BY (chief_of = tribe_id) DESC, user_name;
will retrieve the users in alphabetical order, except the chief, who, if present, will come first.
As for the acceptance into the tribe, this identifies three states: a user is not in a tribe, a user is in the tribe, a user asked to be in a tribe. The first two are actually two faces of the same attribute member_of. So naturally we might create a new attribute and call it wants_in. It would map to a table identical to member_of.
A chief could retrieve all tuples of wants_in whose tribe_id is equal to his own chief_of (so if it's NULL, meaning he's not a chief, this will automatically return nothing). Then he might see this as a table of checkboxes with user names. When he approves the join, for each approval you delete the tuple from wants_in and put it into member_of.
Or you might decide that "membership" is a state in itself, so that you have a more complex join table
user_id,
tribe_id,
status
where status could be, say,
nothing (there's no (U, T, ?) tuple): user U and tribe T are unknown to each other
100: user U is full member of tribe T
-1 : tribe T has decided that U is not a member and cannot even ask to be.
0: user U wants to be member of T
1-99: user U is a probationary (apprentice) member.
It sounds like you'll need two classes: Villager and Tribe.
Then, maybe a database field called chief_of or something that is null if the person is not a chief, and contains the name of their tribe that they are the chief.
In the classes you can have a getChiefOf() method that can be tested to see if the user is a chief or not.
Alternatively, you could have a table of chiefs, indexed by UID, with the same column that says which tribe they're chief of. A little less efficient but a better structure. A drawback that jumps to mind is if the UID for some reason is changed, two tables would have to be updated, so maybe the first one's better.
The site I'm working on has 3 different types of users: admin, applicants, reviewers. Each of these groups will have some basic info that will need to be stored (name, id, email, etc) as well as some data that is unique to each. I have created a users table as well as a table for each of the specific groups to store their unique data.
users: id, f_name, l_name, email, user_type
users_admin: id, user_id, office, emp_id
users_applicant: id, user_id, dob, address
users_reviewer: id, user_id, active_status, address, phone
If a user with user_type of "1" (applicant) logs in I will need to JOIN to the users_applicants table to retrieve their full record. I tried using a UNION but my tables have vastly different columns.
Is there a way to, based on a user's type, write a conditional query that will JOIN to the correct table? Am I going about this completely the wrong way?
Thank's in advance for your help!
Well, in the end your tables are already flawed. Why even have a table for each type? Why not put all those fields into the users table, or maybe a user_details table (if you really want an extra table for non-general data fields)? Currently, you're actually creating 4 independent user tables from a relational point of view.
So why do the type-tables have a surrogate key? Why isn't the user_id already the (only) primary key?
If you changed that, all you would need is the user id to retrieve the data you want, and you've already got that (or you wouldn't even be able to retrieve the user type).
Either you do it programmatically, or you can do this with a series of CASEs and LEFT JOINs.
For simplicity's sake let's do this with a table users where you can have a user of type 1 (normal user), 2 (power user) or 3 (administrator). Normal users have an email but no telephone, power users have an address and a field dubbed "superpower", and administrators have a telephone number and nothing else.
Since you want to use the same SELECT for all, of course you need to place all these in your SELECT:
SELECT user.id, user.type, email, address, superpower, telephone
and you will then need to LEFT JOIN to recover these
FROM user
LEFT JOIN users_data ON (user.id = users_data.user_id)
LEFT JOIN power_data ON (user.id = power_data.user_id)
LEFT JOIN admin_info ON (user.id = admin_info.user_id)
Now the "unused" fields will be NULL, but you can supply defaults:
SELECT
CASE WHEN user.type = 0 THEN email ELSE 'nobody#nowhere.com' END AS email,
CASE WHEN user.type = 1 OR user.type = 2 THEN ... ELSE ... END as whatever,
...
Specific WHERE conditions you can put in the JOIN itself, e.g. if you only want administrators from the J sector, you can use
LEFT JOIN admin_info ON (user.id = admin_info.user_id AND admin_info.sector = 'J')
The total query time should not be too bad, seeing as most of the JOINs will return little (and, if you specify a user ID, they will actually return nothing very quickly).
You could also do the same using a UNION, which would be even faster:
SELECT user.id, 'default' AS email, 'othermissingfield' AS missingfieldinthistable,
... FROM user JOIN user_data ON (user.id = user_data.user_id)
WHERE ...
UNION
SELECT user.id, email, 'othermissingfield' AS missingfieldinthistable,
... FROM user JOIN power_data ON (user.id = power_data.user_id)
WHERE ...
UNION
...
Now, if you specify the user ID, all queries except one will fail very fast. Each query has the same WHERE repeated plus any table-specific conditions. The UNION version is less maintainable (unless you generate it programmatically), but ought to be marginally faster.
In all cases, you'll be well advised in keeping updated indexes on the appropriate fields.
Instead i will suggest you reconstruct you tables structure like this.
Create a table
users_types :
id
type
Then create another table users with a foreign key
users :
id
f_name
l_name
email
office
emp_id
dob
address
active_status
phone
users_types_id
And now when you need to insert data insert null in the fields which are not required for a particular user. And you can simply fetch records on the basis of id. Also using left join will give you the name of user type.
For a registration page where the user enters their contact information and their username and password. I have two tables, one for a username and password with a foreignkey called contactinfo and a table of contact information that i want to join with the users table? Should both foreign and primary key also be set to auto increment?
Thanks
Set both primary keys to be auto increment. Add foreign key from user table to contact info table and your done. Foreign keys are never auto increment, after all they are meant to refer to a value in another table.
Note that if you are using an ORM like Doctrine the relationships between user and contactinformation will be handled automatically for you. Otherwise, you should insert your contact information first, get its generated primary key (via mysql_insert_id for example), then perform your insert into the user table.
user's table should have:
userid (primary, autoincrement)
username
password
the other table has:
infoid (primary, autoincrement)
userid (this will link this row to the other table)
and your data...
you should use a "left join"
The LEFT JOIN keyword returns all rows from the left table (table_name1), even if there are no matches in the right table (table_name2). (w3schools)
so we have a users table as our "left table" and info as "right table". user may not have info set just yet, but he/she has an account. so we have an account ("left") but not necessarily info ("right") - the LEFT JOIN sounds like the join for the job
and your sql should roughly look like
SELECT "columns" FROM table1
LEFT JOIN table2 ON table1.userid = table2.userid
//where "columns" are the columns you want to get
//from the tables in "table.column" notation like: user.username or info.mobile