Mysql table relations - mysql

I have read various topics regarding table relations and while i am building my database i am a bit confused on what should i do.
I have 3 type of registration on my site(artist, fan, companies). Each registered user gets a unique key and username and the appropriate type of user (ex. fan). I am trying to involve music genres to all types of registration but genres will also be added to uploaded music files. At the moment i am storing one music genre per track and user by an array list that is shown in a form. Then system is storing it to the appropriate field. But i want some users to have more than 1 genres stored.
Now what i have done is below:
Users table (total 14 columns)
ID | username | email | password | type | signup | lastlogin | etc.
Settings table (total 10 columns)
ID | username | description | banner | country | genres | avatar etc.
Music table
ID | username | artist | title | maingenre | othergenre | cover | fileurl
By having in mind performance and let's assume that thousand of thousand users is registering...
Should i just add all settings column in the users table or it's ok to keep as i have them now? Settings can be updated by user while users table is updated by the system.
Should i split the user table according to users type? For example Artist table, fan table etc. that will store the appropriate registration and settings? Type of user needs to be in a column as is important for some functions of the site.
Regarding music table i was thinking to making a table for each genre that will store the uploads according to the genre specified by the user. Is this a good way of storing tracks to database or not? So when i want to call tracks of disco music i just use the disco music table.
Any help will be much appreciated.

not quiet sure I understand everything completely how your table is correlated, or what exactly you want or plan to do, but here is one idea about how to store genres in your database. And to connect it with your Setting table
First create table Genres in which you will store all genres. That table could look like this
Table: Genres
ID | genres_name | description etc.
ID - will be primary key auto increment
genres_name - will hold the name of genre (blues, jazz, disco...)
description - this column i added just if you want to add something specific by every genre it's not necessary
Next step is to create table Settings_genres. This table will store relation between your Setting table and Genres table and will look like this
Table: Settings_genres
settings_id | genres_id
So data in this table will look like this (for the setting ID 1 which will have 3 different Genres)
settings_id | genres_id
------------------------
1 | 2
------------------------
1 | 4
------------------------
1 | 5
------------------------
settings_id and genres_id will be primary key pair which means that you wont be able to store two identical pair int this table (You can have only one relation between one settings column and one genre column)
That is something called Many to many relationship and I'm sure that you can easily find more about that if you google it just a little.
When you want to pull data off from database which will show all settings and all genres you can do it with query like this
SELECT Settings.*, Genres.genres_name, Genres.description
FROM Settings
INNER JOIN Settings_Genres
ON Settings.ID = Settings_Genres.settings_id
INNER JOIN Genres
ON Settings_Genres.genres_id = Genres.ID
ORDER BY ID
Here is SQL Fiddle to see how it's look like.
When you want to pull data from settings table where that table is connected with specific genre you do that like this
SELECT Settings.*, Genres.genres_name, Genres.description
FROM Settings
INNER JOIN Settings_Genres
ON Settings.ID = Settings_Genres.settings_id
INNER JOIN Genres
ON Settings_Genres.genres_id = Genres.ID
WHERE Genres.genres_name = 'Rock'
ORDER BY ID;
This can also be achieved by this query which may be a little faster but let's not go into detail...
SELECT Settings.*, Genres.genres_name, Genres.description
FROM Settings
INNER JOIN Settings_Genres
ON Settings.ID = Settings_Genres.settings_id
INNER JOIN Genres
ON Settings_Genres.genres_id = Genres.ID
AND Genres.genres_name = 'Rock'
ORDER BY ID;
Here is FIDDLE for that...
So basically I suggest you to learn a little bit about relation between tables especially many to many relationship. And on than you will see how to improve your data table design.
Hope I help a little.
GL!

i think the way your table is, is okay. you dont have to split the table based on the type of users you have. but i think what you could use is font end technologies to allow users preform activities you want them, which is restricting them to only what you want them to do, they by controlling flow of information within the system. i hope that helps.

Related

Displaying dynamic data based on user saved habits

I have a database table which contains recipe information, I then have a second table which contains user information and finally I have another table which is a junction table between the two containing recipe_id and user_id which is used to hold the user's saved recipes.
My tables look like this:
|user |
|user_id |
|user_name |
|user_email |
|user_password|
|recipe|
|recipe_id|
|recipe_name|
|recipe_descript|
|category_id|
| etc.|
|user_saved_recipes|
|user_saved_recipe_id|
|user_id|
|recipe_id|
|category|
|category_id|
|category_name|
I want to be able to SELECT recipes from the recipe table that are LIKE recipes from the user_saved_recipes table based on a particular user. If anyone could help me go about this I would be very grateful as I have been struggling to find a solution.
Basically I want to be able to get a list of recipes that are similar to those recipes the user has saved thus creating suggestions for the user.
This is what I have tried so far:
SELECT *
FROM recipe r
JOIN user_saved_recipes ur
ON r.recipe_id = ur.recipe_id
JOIN user u
ON ur.user_id = u.user_id
WHERE ur.user_id = 1;
I know I need a LIKE in there somewhere in order to return rows with a similar name but I am unsure of how to go about this as I only have recipe_id in the user_saved_recipes junction table.
Thanks!
Recipes that are "like" other recipes is a very vague request. You have to consider - and specify - how you will calculate "like" in this case. What are some typical use cases you wish to meet? e.g.
Consider that someone really likes rabbit as an ingredient;
"You may also like" suggestions from your software should include other rabbit recipes.
So matching on ingredients (particularly the "hero" ingredient) is very likely to part of your algorithm.
Similarly, if a person has saved several cakes in their favourites, then the category cake will likely be a useful part of that algorithm.
Basically you need to compute a rank you can apply to recipes not already in their favourites that share some characteristics with those within the favourites.

Any way to "shortcut" common joins or code blocks in MySQL?

I have my tables in fairly normalized forms (I've only just started learning this, so I would wildly guess that I'm using 3rd normal form but I also know that I'm probably wrong, just as I know that some of my example code will be horribly written and also potentially wrong), and there are a few very common joins that I use across several queries.
For illustration's sake, let's say I have information on songs, something like this:
Songs:
song_id (PK) | song_title | first_artist | most_famous_artist
Albums:
album_id (PK) | album_title | version | format | year_released
Song versions:
song_version_id (PK) | song_id (FK) | artist | year_released
Album tracks:
album_id (PK, FK) | track_no (PK) | song_version_id (FK)
And suppose that I am frequently pulling up information on a particular version of a song, including the name of the song and the album it appeared on, so I keep writing:
FROM
song_versions as sv
INNER JOIN songs as s on sv.song_id = s.song_id
INNER JOIN album_tracks as at on at.song_version_id = sv.song_version_id
INNER JOIN albums as a on a.album_id = at.album_id
or whatever the case may be. I know I could denormalize a bit, and either combine a couple of tables together or have some data duplicated across tables and make sure I keep them consistent. But is there a way to keep the data structure the same, but have a shortcut to refer to the combination of song/version/album without having to write the same join all the time? (Especially if I may often want to only refer to particular subsets of that same join - e.g. the albums containing the most famous version of a particular song, or songs that have appeared on an album released after 2000.)

Avoiding JOIN to increase performance?

Lets say I have users table:
| id | username | email | address |
And posts table:
| id | post | user_id | date |
When I want to show posts, each time I need to go users table to retrieve username from user_id. I want to avoid using JOIN for this simple data retreive so what I do is adding another coloumn to posts table:
| id | post | user_id | username | date |
This way I will not have to use JOIN to retreive username when showing posts
Do you think that this is better?
no. your alternative structure is vulnerable to inconsistencies (e.g. if a user changes his name; read about 3rd Normal form here http://en.wikipedia.org/wiki/Third_normal_form#.22Nothing_but_the_key.22)
why don't you want to use JOINs? have you set up approriate indexes?
I think it depends on the design and future, niy I will suggest you not to do that:
although from present respect, you will think it will be better performance to avoid join, but what if your application expand, and it is no good to use this unnormalized table structure.
For instance, if one of the poster changed username, how could you achieve that? to update the whole table? if your data could exccess 10Million tuples, it will be tough because update will lock the table in the process of updating.
thus I will not recommend this.
Join performance can be omit if your application needs frequently updating in that way.
If the [id] of [users] table is the primary key, I think it is good enough to use JOIN.
Alternatively, if you select limited number of posts, such as 10 posts, can also try this sql:
select id, post, user_id,
(select username from users where id = user_id) as username, date
from posts
limit 0, 10

database design for user subscriptions

So I'm new to databases in the scope of the subject and looking for some advice for what I am sure is fairly simple. first I'm using MySql as my db I currently have two tables one for storing user accounts and details :
TABLE user
id | username | password | email_address | user_devices | contact_method
and another for storing video content by producers which looks like:
TABLE series
id | series_title | still_broadcasting | last_updated |
I would like to implement a feature where Users can select series which they wish to be notified of when new releases are made available and also select how to be notified about these releases (email or push notification ) and how often to be notified (on arrival, hourly, daily, weekly ) I am wondering whats the best way to go about doing this?
I've thought of these ideas by myself but am looking for a second opinion/ better way altogether: (all ideas minus 4 involve storing how to notify user along with how often in user table)
adding a text column to user table called following and just having csv's for each series
adding multiple boolean column's to user table one for each series
adding text column to series table with csv's of user's Id numbers following series
creating an entirely new table for notifications though i don't really see the purpose of this as its very redundant
I then plan to just add cron jobs to my server to actually go about regulaurly sending notifications to user's
Thanks in advance for any help.
First of all, it might be worth giving some articles on basic database design a read. A quick google turned up this which covers identifying relationships
http://www.datanamic.com/support/lt-dez005-introduction-db-modeling.html
Your best bet is to use a linking table i.e.
CREATE TABLE userHasSeries (
userID INT,
seriesID INT
);
This can then be used in an INNER JOIN query to get the users choices. What you are doing here is an n:m link between 2 tables. An example inner join would be
SELECT
u.id AS userID,
u.username,
s.seriesID,
s.series_title,
s.still_broadcasting,
s.last_updated
FROM users AS u
INNER JOIN userHasSeries AS uhs
ON uhs.userID = u.id
INNER JOIN series AS s
ON s.id = uhs.seriesID
If users.user_devices is also a comma seperated list I would advise heavily that you adopt a similar n:m approach there also.
A partial answer which complements what has been written in other answers:
Don't keep a list of devices in the 'user_devices' field - break this out into a separate table. In fact, you'll need two tables: one to list the various devices, and one a join table which has two fields: user_id and device_id. This will enable you to track which user has which device, but also to provide a list of users per device.
If I were you I would add a third table as following:
TABLE user
id | username | password | email_address | user_devices | contact_method |notification_type
TABLE series
id | series_title | still_broadcasting | last_updated
TABLE followings
id | user_id | series_id
In notification_type I would put (on arrival, hourly, daily, or weekly), now in the followings tables I will store all the user's preferred series.
Doing this way makes easy to add, delete, update, or select all user's preferred series. All will be simple SQL queries. Also you avoid parsing comma separated strings.
for example, if you want to get all preferred series of an user:
SELECT * FROM followings AS f INNER JOIN series AS s ON f.series_id = s.id WHERE f.user_id = ?
if want to get all users that prefer a serie:
SELECT * FROM followings AS f INNER JOIN user AS u ON f.user_id = u.id WHERE f.series_id = ?

Table with a lot of attributes

I'm planing to build some database project.
One of the tables have a lot of attributes.
My question is: What is better, to divide the the class into 2 separate tables or put all of them into one table. below is an example
create table User { id, name, surname,... show_name, show_photos, ...)
or
create table User { id, name, surname,... )
create table UserPrivacy {usr_id, show_name, show_photos, ...)
The performance i suppose is similar due to i can use index.
It's best to put all the attributes in the same table.
If you start storing attribute names in a table, you're storing meta data in your database, which breaks first normal form.
Besides, keeping them all in the same table simplifies your queries.
Would you rather have:
SELECT show_photos FROM User WHERE user_id = 1
Or
SELECT up.show_photos FROM User u
LEFT JOIN UserPrivacy up USING(user_id)
WHERE u.user_id = 1
Joins are okay, but keep them for associating separate entities and 1->N relationships.
There is a limit to the number of columns, and only if you think you might hit that limit would you do anything else.
There are legitimate reasons for storing name value pairs in a separate table, but fear of adding columns isn't one of them. For example, creating a name value table might, in some circumstances, make it easier for you to query a list of attributes. However, most database engines, including PDO in PHP include reflection methods whereby you can easily get a list of columns for a table (attributes for an entity).
Also, please note that your id field on User should be user_id, not just id, unless you're using Ruby, which forces just id. 'user_id' is preferred because with just id, your joins look like this:
ON u.id = up.user_id
Which seems odd, and the preferred way is this:
ON u.user_id = up.user_id
or more simply:
USING(user_id)
Don't be afraid to 'add yet another attribute'. It's normal, and it's okay.
I'd say the 2 separate tables especially if you are using ORM. In most cases its best to have each table correspond to a particular object and have its field or "attributes" be things that are required to describe that object.
You don't need 'show_photos' to describe a User but you do need it to describe UserPrivacy.
You should consider splitting the table if all of the privacy attributes are nullable and will most probably have values of NULL.
This will help you to keep the main table smaller.
If the privacy attributes will mostly be filled, there is no point in splitting the table, as it will require extra JOINs to fetch the data.
Since this appears to be a one to one relationship, I would normally keep it all in one table unless:
You would be near the limit of the number of bytes that can be stored in a row - then you should split it out.
Or if you will normally be querying the main table separately and won't need those fields much of the time.
If some columns is (not identifiable or dependent on the primary key) or (values from a definite/fixed set is being used repeatedly) of the Table make a Different Table for those columns and maintain a one to one relationship.
Why not have a User table and Features table, e.g.:
create table User ( id int primary key, name varchar(255) ... )
create table Features (
user_id int,
feature varchar(50),
enabled bit,
primary key (user_id, feature)
)
Then the data in your Features table would look like:
| user_id | feature | enabled
| -------------------------------
| 291 | show_photos | 1
| -------------------------------
| 291 | show_name | 1
| -------------------------------
| 292 | show_photos | 0
| -------------------------------
| 293 | show_name | 0
I would suggest something differnet. It seems likely that in the future you will be asked for 'yet another attribute' to manage. Rather than add a column, you could just add a row to an attributes table:
TABLE Attribute
(
ID
Name
)
TABLE User
(
ID
...
)
TABLE UserAttributes
(
UserID FK Users.ID
Attribute FK Attributes.ID
Value...
)
Good comments from everyone. I should have been clearer in my response.
We do this quite a bit to handle special-cases where customers ask us to tailor our site for them in some way. We never 'pivot' the NVP's into columns in a query - we're always querying "should I do this here?" by looking for a specific attribute listed for a customer. If it is there, that's a 'true'. So rather than having these be a ton of boolean-columns, most of which would be false or NULL for most customers, AND the tendency for these features to grow in number, this works well for us.