MySQL database Schema for two relational tables - mysql

I'm trying to think on the most performant database schema for a specific data structure. There are two main entities: Courses and Themes. A Course is a collection of Themes. A Theme have fields like Videos, Resources and Video Total Time.
Visually representing this data structure:
- Course
|_ ID: 12345
|_ Themes: [A, B] (an array of UIDs)
- Theme A
|_ Courses: [12345,67890] (an array of UIDs)
|_ Videos: [1,2,3,4,5,7] (an array of UIDs)
|_ Resources: [10,11,12] (an array of UIDs)
|_ Video Total Time: 10000 (probably stored as seconds as tinyint field)
- Theme B
|_ Courses: [12345,98765] (an array of UIDs)
|_ Videos: [5,6,7,8] (an array of UIDs)
|_ Resources: [12,13,14] (an array of UIDs)
|_ Video Total Time: 20000 (probably stored as seconds as tinyint field)
What I'm trying to achieve is a database schema for two tables, one for Courses, and one for Themes. The idea would be to have a MySQL query that gets a Course and group all fields from the Themes. In other words, when I get the result of the MySQL query, (using PHP) I'll get an array or object like this:
Array(
'ID' => 12345
'themes' => [A,B]
'videos' => [1,2,3,4,5,6,7,8]
'resources' => [10,11,12,13,14]
'video_total_time' => 30000
)
So, the point is that they are two relational databases. When I send a query to the DB requesting data from the video, I need to pull data from all the themes, and merge them together.
Since I'm not an expert on SQL / MySQL, I'm trying to learn a little bit about it while I try to figure out:
1) What is the best database schema for these two entities? Courses and Themes? Thinking specially about performance
2) Can I get the final data all using SQL? Or should I pull some data from the database, and then parse the data with PHP? What is usually faster?
3) What is the best way to store the array of UIDs? As a string? Or there's a better way to store it?
The primary goal of this is performance. I have this kind of data in a different database schema, merged with thousands of other kinds of data (WP databases, wp_posts / wp_postmeta tables), but right now it's really slow to get the information I need.
Any tips and suggestions are more than welcome!
Edit: Solved!
It was a tough call to decide which answer suits best my needs, because #TimMorton's and #PaulSpiegel's answers lead us to the same path, but with slightly different approaches. Tim's answer is great to understand how to properly design database schemas, taking into account many-to-many relationships, and how to organize your queries. But since the main focus of this question is improve performance, Paul's answer is more focused on that, with specific details about primary keys and indexes (which are fundamental to improve performance of the queries).
Anyways, I learned a lot about designing a database schema. Here's the lessons I learned:
Don' try to stuff everything into the same table: it is fundamental to identify the entities properly before defining which tables you need. I started with two tables, for Videos and Themes. But turns out that a proper DB schema for my specification includes tables for Videos and Resources.
Don't store arrays into columns: use a proper strategy to define the relation between entities. If you have one-to-one or one-to-many relationships, use the entities IDs and foreign keys. If you have many-to-many relationships, then the proper design pattern is to create a dedicated table only to create relations between entities. This will allow you to use JOIN clauses into your queries to put all the data together.
When you think about performance, think about INDEX: depending on your table structure, using either an index or a composite index can improve query performance.
Don't try to get everything in one big query: you definitely can, but having separate queries for sections of the data you need (on my example, one query to get all themes for a course, one to get all videos for the course, one to get resources for the course) pays off with code organization and readability.
I don't know if I'm correct with everything above, but it's what I learned so far. Hope this helps someone else too.

Creating the schema
Step 1: Identify entities and their attributes
Course (ID, title, description)
Theme (ID, title, description)
Video (ID, title, description, duratation)
Ressource (ID, title, url)
Step 2: Identify relations
Theme => Course
Video => Theme
Ressource => Theme
Step 3: Create tables
courses
ID (PK)
title
description
themes
ID (PK)
course_id (FK)
title
description
videos
ID (PK)
theme_id (FK)
title
description
duratation
ressources
ID (PK)
theme_id (FK)
title
url
If themes can share videos and ressources, then it would be many-to-many relations.
In this case you would need separate tables for those relations.
Remove the theme_id column from videos and ressources and add the following tables:
themes_videos
theme_id (PK) (FK)
video_id (PK) (FK)
themes_ressources
theme_id (PK) (FK)
ressource_id (PK) (FK)
Here you should define composite primary keys on (theme_id, video_id) and (theme_id, ressource_id).
Also create reverse indexes on (video_id, theme_id) and (ressource_id, theme_id).
Retrieving data
Assuming you know the ID of the course (which is 123),
you can then retrieve the related data (from the many-to-many schema)
with the following queries (which you execute one by one):
select c.*
from courses c
where c.id = 123;
select t.*
from themes t
where t.course_id = 123;
select distinct v.*
from themes t
join themes_videos tv on tv.theme_id = t.id
join videos v on v.id = tv.video_id
where t.course_id = 123;
select distinct r.*
from themes t
join themes_ressources tr on tr.theme_id = t.id
join ressources r on r.id = tr.ressource_id
where t.course_id = 123;
Then compose your array/object from retrieved data in PHP.
Performance
Trying to get all data with a single SQL query is not always a good idea.
You just make your code and schema too complicated.
Executing a couple of queries is not the end of the world.
What you should avoid, is running executing a query in a loop
(like: for each theme select related videos).

In it's simplest form, assuming no many to many relationships:
Course Theme
-------- --------
CourseID <--+ ThemeId
Name | Name
+------ CourseID
|
|
| Video
| --------
| VideoID
| Name
| Length
+------ CourseID
|
|
| Resource
| --------
| ResourceID
| Name
+------ CourseID
In this form, a Course can have many themes, many videos, and many resources; but each theme, video, and resource can have only one course.
However, I don't think that's how you want it.
I would lean more towards
Course Theme
-------- --------
+----> CourseId +---> ThemeId
| Name | Name
| ThemeId ----+
|
|
| Video
| --------
| VideoID
| Name
| Length
+------ CourseID
|
|
| Resource
| --------
| ResourceID
| Name
+------ CourseID
This allows a course to have only one theme, but many videos and resources. This allows the themes to have more than one course.
But it still doesn't quite fit the bill...
This one allows many courses to share the same theme, as well as have more than one theme:
Course Course_Theme Theme
-------- ------------ --------
+----> CourseId <----- CourseId +--> ThemeId
| Name ThemeId ---+ Name
| ThemeId
|
|
| Video
| --------
| VideoID
| Name
| Length
+------ CourseID
|
|
| Resource
| --------
| ResourceID
| Name
+------ CourseID
As this stands now, each course can have many themes, videos, and resources.
Each theme can have many courses.
Each video and resource belongs to a course (i.e., can have only one course)
If a video or resource can be for more than one course, then you'll have to expand it just as I did with themes.
As per comment, everything is many to many. Notice I don't have any direct relations between themes and videos nor themes and resources. I don't think they will be necessary; you should be able to pick up what you need going through courses.
Course Course_Theme Theme
-------- ------------ --------
+----> CourseId <---- CourseId
| Name ThemeId ----------> ThemeId
| Name
|
| Course_Video Video
| ------------ --------
+---------------------- CourseId
| VideoId ----------> VideoId
| Name
| Length
|
| Course_Resource Resource
| --------------- --------
+----------------------- CourseId
ResourceId -------> ResourceId
Name
Url, etc.
Now for the queries. Although it is possible to use aggregate functions along with group by, I think it makes far more sense to keep it simple and just pull things out one at a time.
Themes per course
SELECT T.*
FROM COURSE C
INNER JOIN COURSE_THEME CT ON CT.COURSEID=C.COURSEID
INNER JOIN THEME T ON CT.THEMEID=T.THEMEID
WHERE {insert your search conditions on course}
or, if you know CourseId:
SELECT T.*
FROM THEME T
INNER JOIN COURSE_THEME CT ON T.THEMEID = CT.THEMEID
WHERE CT.COURSEID = ?
likewise,
Videos per course
SELECT V.*
FROM COURSE C
INNER JOIN COURSE_VIDEO CV ON CV.COURSEID=CV.COURSEID
INNER JOIN VIDEO ON CV.VIDEOID=V.VIDEOID
WHERE {insert your search conditions on course}
or, if you know the CourseId:
SELECT V.*
FROM VIDEO V
INNER JOIN COURSE_VIDEO CV ON CV.VIDEOID = V.VIDEOID
WHERE CV.COURSEID = ?
to select the sum of the video lengths per course,
SELECT SUM(LENGTH) AS TOTAL
FROM VIDEO
INNER JOIN COURSE_VIDEO CV ON CV.VIDEOID = V.VIDEOID
WHERE CV.COURSEID = ?
GROUP BY CV.COURSEID
Now, the tricky part is videos per theme. I am making an assumption here: the set of videos per theme is the same as the set of videos per course per theme.
The long way around:
SELECT V.*
FROM VIDEO V
INNER JOIN COURSE_VIDEO CV ON VIDEO.VIDEOID = CV.VIDEOID
INNER JOIN COURSE C ON COURSEID = CV.COURSEID
INNER JOIN COURSE_THEME CT ON C.COURSEID = CT.COURSEID
INNER JOIN THEME T ON CT.THEMEID = T.THEMEID
WHERE THEMEID = ?
Blech. You can cut out the middlemen:
SELECT V.*
FROM VIDEO V
INNER JOIN COURSE_VIDEO CV ON VIDEO.VIDEOID = CV.VIDEOID
INNER JOIN COURSE_THEME CT ON CV.COURSEID = CT.COURSEID
WHERE CT.THEMEID = ?
When you have your tables normalized, you can get any piece of information from whatever starting point you choose. FWIW, your example is a fairly complicated one since everything is many to many relations.
Update
Even though I had courses as the root, even when themes are the root things don't change much:
Theme Course_Theme Course
-------- ------------ --------
+----> ThemeId <---- ThemeId
| Name CourseId ---------> CourseId
| Name
|
| Theme_Video Video
| ------------ --------
+---------------------- ThemeId
| VideoId ---------> VideoId
| Name
| Length
|
| Theme_Resource Resource
| -------------- --------
+----------------------- ThemeId
ResourceId ------> ResourceId
Name
Url, etc.
In this configuration, courses have videos and resources through ThemeId, i.e.:
SELECT V.*
FROM COURSE_THEME CT
INNER JOIN VIDEO_THEME VT ON VT.THEMEID = CT.THEMEID
INNER JOIN VIDEO V ON V.VIDEOID = VT.VIDEOID
WHERE CT.THEMEID = ?

Table Structure
Make tables like image as shown and use json encode/decode time of input/out. In the query you can have total time from the table.

Related

MySQL - Advice linking Multiple Tables - Is this right?

Would someone mind advising me please regarding this table setup.
Its the first time designing a database. This will be a part of it.
Its a report writing application. Multiple Engineers can be assigned to attend any job/report and multiple engineers can author the report as well as attending.
Is this the best way to do this. I would need to be able to search attendees and authors separately in the application.
Thanks very much for the assistance.
You have, I believe, two tables containing entities. The entities are employee and report.
These entities have two different many-to-many relationships: author and attendee.
So your tables are these
employee report
-------- -----
employee_id (PK) report_id (PK)
surname title
givenname releasedate
whatever whatever
Then you have two many:many relationship tables with the same columns as each other. One is author and the other is attendee.
author / attendee
------
employee_id PK, FK to employee.employee_id
report_id PK, FK to report.report_id
Notice the compound (two-column) primary keys.
+---------------------+\ /+-------------+\ /+-----------------------+
| +-----+ author +-----+ |
| |/ \+-------------+/ \| |
| employee | | report |
| | | |
| |\ /+-------------+\ /| |
| +-----+ attendee +-----+ |
+---------------------+/ \+-------------+/ \+-----------------------+
\ /
----- means a many-to-many relationship
/ \
When you determine an employee is an attendee for a certain report, you insert a row into the attendee table with the correct employee and report.
If you want, for example, all authors for each report you can do this sort of thing:
SELECT r.title, r.releasedate,
GROUP_CONCAT(e.surname ORDER BY e.surname SEPARATED BY ',')surnames
FROM report r
LEFT JOIN author a ON r.report_id = a.report_id
LEFT JOIN employee e ON a.report_id = e.report_id
GROUP BY r.title, r.releasedate
ORDER BY r.releasedate DESC
The LEFT JOIN operations allow your query to find reports that have no authors. Ordinary inner JOIN operations would suppress those rows from your result set.
There is a limitation with this strict E:R design. For many kinds of reports, (scientific papers for example) the order of authors is critically important. (You want to start an academic food fight? List the authors of a paper in the wrong order.)
So you author table might also contain an ordinal value.
author
------
employee_id PK, FK to employee.employee_id
report_id PK, FK to report.report_id
ordinal INT
and your report query would contain this line.
GROUP_CONCAT(e.surname ORDER BY e.ordinal SEPARATED BY ',')surnames

MySQL - Best method for selecting data for this Hierarchical Model?

Suppose I had these 4 tables, consisting of various foreign key relationships (eg a area must belong to a location, a shop must belong to area, an item price must belong to a shop ect..)
----------------------------------
|Location Name | Location ID |
| | |
----------------------------------
-------------------------------------------------
|Area Name | Area ID | Location ID |
| | | |
-------------------------------------------------
-------------------------------------------------
| Shop Name | Shop ID | Area ID |
| | | |
-------------------------------------------------
----------------------------------
| Item Price | Shop ID |
| | |
----------------------------------
And I wanted the sum of 'Item Price' that belonged to a specific location id. So all the areas and shops item price total for location id 'x'.
One way I found to do this is to join all the tables for one location and get the amount eg:
SELECT SUM(Item Price) FROM
items
left join shops ON (items.shop id = shops.shop id)
left join areas ON (shops.area id = areas.area id)
left join locations ON (areas.location id = location.location id)
WHERE Location Id = 4;
However is this the best way to do this since it involves retrieving the full tree of the data and filtering it out? Would there be a better way if there are a million rows or is this the best way?
You can try sub query --
SELECT SUM(Item Price) FROM
items
left join shops ON (items.shop id = shops.shop id)
left join (select area id from areas where Location Id = 4) as Ar ON (shops.area id = areas.area id)
If you define the right indexes, then the query does not read all the millions of rows for each table.
Think about a telephone book and how you look up a name. Do you read the whole book cover to cover looking for the name? No, you take advantage of the fact that the book is sorted by lastname, firstname and you go directly to the name. It takes only a few tries to find the right page. In fact, on average it takes about log2N tries for a book with N names in it.
The same kind of search happens for each join. If you have indexes, each comparison expression uses a similar lookup to find matching rows in the joined table. It's pretty fast.
But if that's not fast enough, you can also use denormalization, which in this case would be storing all the data in one table, with many columns wide.
----------------------------------------------------------------------
|Location Name | Area Name | Shop Name | Item Name | Item Price |
| | | | | |
----------------------------------------------------------------------
The advantage of denormalization is that it avoids certain joins. It stores the row just like one of the rows you'd get from the result set of your example joined SQL query. You just read one row from the table and you have all the information you need.
The disadvantage of denormalization is the redundant storage of data. Presumably each shop has many items. But each item is stored on a row of its own, which means that row has to repeat the names of the shop, area, and location.
By storing those data repeatedly, you create an opportunity for "anomalies" like if you change the name of a given shop, but you mistakenly change it only on a few rows instead of everywhere the shop name appears. Now you have two names for the same shop, and someone else looking at the database has no way of knowing which one is correct.
In general, maintaining multiple normalized tables in preferable, because each "fact" is stored exactly once, so there can be no anomalies.
Creating indexes to help your queries is sufficient for most applications.
You might like my presentation, How to Design Indexes, Really, and the video: https://www.youtube.com/watch?v=ELR7-RdU9XU

MySQL query get column value similar to given

Sorry if my question seems unclear, I'll try to explain.
I have a column in a row, for example /1/3/5/8/42/239/, let's say I would like to find a similar one where there is as many corresponding "ids" as possible.
Example:
| My Column |
#1 | /1/3/7/2/4/ |
#2 | /1/5/7/2/4/ |
#3 | /1/3/6/8/4/ |
Now, by running the query on #1 I would like to get row #2 as it's the most similar. Is there any way to do it or it's just my fantasy? Thanks for your time.
EDIT:
As suggested I'm expanding my question. This column represents favourite artist of an user from a music site. I'm searching them like thisMyColumn LIKE '%/ID/%' and remove by replacing /ID/ with /
Since you did not provice really much info about your data I have to fill the gaps with my guesses.
So you have a users table
users table
-----------
id
name
other_stuff
And you like to store which artists are favorites of a user. So you must have an artists table
artists table
-------------
id
name
other_stuff
And to relate you can add another table called favorites
favorites table
---------------
user_id
artist_id
In that table you add a record for every artist that a user likes.
Example data
users
id | name
1 | tom
2 | john
artists
id | name
1 | michael jackson
2 | madonna
3 | deep purple
favorites
user_id | artist_id
1 | 1
1 | 3
2 | 2
To select the favorites of user tom for instance you can do
select a.name
from artists a
join favorites f on f.artist_id = a.id
join users u on f.user_id = u.id
where u.name = 'tom'
And if you add proper indexing to your table then this is really fast!
Problem is you're storing this in a really, really awkward way.
I'm guessing you have to deal with an arbitrary number of values. You have two options:
Store the multiple ID's in a blob object in JSON format. While MySQL doesn't have JSON functions built in, there are user defined functions that will extract values for you, etc.
See: http://blog.ulf-wendel.de/2013/mysql-5-7-sql-functions-for-json-udf/
Alternatively, switch to PostGres
Add as many columns to your table as the maximum number of ID's you expect to have. So if /1/3/7/2/4/8/ is the longest entry, have 6 columns in your table. Reason this is bad: you'll have sparse columns that'll unnecessarily slow your tables.
I'm sure you could write some horrific regex to accomplish the task, but I caution on using complex regex's on enormous tables.

Correct way of storing a list of related values in Database

Let's say I have the following scenario.
A database of LocalLibrary with two tables Books and Readers
| BookID| Title | Author |
-----------------------------
| 1 | "Title1" | "John" |
| 2 | "Title2" | "Adam" |
| 3 | "Title3" | "Adil" |
------------------------------
And the readers table looks like this.
| UserID| Name |
-----------------
| 1 | xy L
| 2 | yz |
| 3 | xz |
----------------
Now, lets say that user can create a list of books that they read (a bookshelf, that strictly contains books from above authors only i.e authors in our Db). So, what is the best way to represent that bookshelf in Database.
My initial thought was a comma separated list of BookIDin Readers table. But it clearly sounds awkward for a relational Db and I'll also have to split it every time I display the list of users' books. Also, when a user adds a new book to shelf, there is no way of checking if it already exists in their shelves except to split the comma-separated list and and compare the IDs of two. Deleting is also not easy.
So, in one line, the question is how does one appropriately models situations like these.
I have not done anything beyond simple SELECTs and INSERTs in MySQL. It would be much helpful if you could describe in simpler terms and provide links for further reading.
Please comment If u need some more explanation.
Absolutely forget the idea about a comma separated list of books to add to the Readers table. It will be unsearchable and very clumsy. You need a third table that join the Books table and the Readers table. Each record in this table represent a reader reading a book.
Table ReaderList
--------------------
UserID | BookID |
--------------------
You get a list of books read by a particular user with
select l.UserID, r.Name, l.BookID, b.Title, b.Author
from ReaderList l left join Books b on l.BookID = b.BookID
left join Readers r on l.UserID = r.UserID
where l.UserID = 1
As you can see this pattern requires the use of the keyword JOIN that bring togheter data from two or more table. You can read more about JOIN in this article
If you want, you could enhance this model adding another field to the ReaderList like the ReadingDate

Database Design: Multiple tables vs a single table

I am making a website where there are different types of items such as blogs, posts, articles and so on. A user can set any one of them as his/her favorite. Now when I approach this thing, I have two options
Make a table for user favorites for each type of object.
Make a common table for all type of objects for all the users.
The problem with the 1st structure is that I will have to query a lot of tables for displaying the favorites of a particular user. But it will allow me to easily group the favorites into different categories.
However if I have to show all the favorites on one single page and merge them all, sorted according to time, then that becomes difficult. But if I use the second model, I can easily get the latest favorites, and also grouping them according to object type is not difficult, but I will have one large table site wide.
Which of the two strategies will be more scalable.
The 1st one entails multiple database queries, and the second one
entails a large single table.
If it helps, I am using MySql
It seems that you already know the answer, but remember, keep the systems you design simple to modify as business models always change over time or they eventually fail (it's a generalization but you get the idea). A corollary of that is if you make a rigid model, fast or slow, it's rigid, changes will be harder and the end user won't see the difference, hence no money/happiness change is achieved, unless it's a very bad change.
Your problem is not technical in a way a query works on the engine but more of a philosophical one, easy changes versus apparent speed.
Ask yourself, what's the advantage of having a normalized database? Think about a clean architecture and design, performance is the least problem in todays world as processing is cheaper and storage also. But design is expensive.
Normalization was made to make systems that don't depend on last moment decisions but on a structured design process.
Big tables are not a big deal for MySql but they are a big deal to maintain, modify and expand. It's not just adding one more column, it's about the rigid structure of the data itself. Eventually in time you will just add columns that contain indexes, and those indexes will be pointing to small tables. MySql will be plowing it's way around all that data anyway.
So i'll go for the first one, a lot of small tables, many-to-many.
I have this design on my website. My modules are: news, articles, videos, photos, downloads, reviews, quizzes, polls, etc etc. All in separate tables. I have a likes table where users can like or dislike a post (in your case favorites). The query to get these isn't that complicated.
First off for the most part MOST of my tables for the modules are structured the same way:
id
title
content
user_id (author)
date
etc
with a few exceptions being that sometimes title is called question or there is no content column. That does not cause any issues.
My likes tables is set up like this:
id
page_id
module_id (what table did it come from...I have a modules table where each module has a title, associated id, directory, etc)
post_id (corresponds to the module table id)
user_id (user who did the liking or posting)
status (0 = like, 1 = dislike)
date (when the liking/disliking took place)
Modules table example:
id
title
directory
post_type
Example
id title directory post_type
1 News news news
2 Episode Guide episodes episode
3 Albums discography/albums album
Essentially yours would have a similar set up, modifying the table structure as necessary for your needs.
Query to get all the likes or favorites for a particular user:
$getlikes = mysql_query("SELECT DISTINCT post_id, module_id, page_id FROM likes WHERE user_id = $profile_id ORDER BY id DESC LIMIT $offset, $likes_limit", $conn);
$likes = mysql_num_rows($getlikes);
if($likes == "0"){
echo "<br><Center>$profile_username does not have any liked posts at this time.</center><BR>";
}
else {
echo "<table width='100%' cellspacing='0' cellpadding='5'>
<Tr><th>Post</th><th align='center'>Module</th><th align='center'>Page</th><tr>";
while ($rowlikes = mysql_fetch_assoc($getlikes)) {
// echo data
$like_page_id = $rowlikes['page_id'];
$like_module_id = $rowlikes['module_id'];
$like_post_id = $rowlikes['post_id'];
// different modules have different fields for the "title", most are called title but quotes is called "content" and polls is called "questions"
if($like_module_id == "11"){
$field = "question";
}
elseif($like_module_id == "19"){
$field = "content";
}
else{
$field = "title";
}
// FUNCTIONS
PostURL($like_page_id, $like_module_id, $like_post_id);
ModTitle($like_module_id);
ModTable($like_module_id);
ModURL($like_page_id, $like_module_id);
fpgURL($like_page_id);
$getpostinfo = mysql_query("SELECT $field AS field FROM $mod_table WHERE id = $like_post_id", $conn);
$rowpostinfo = mysql_fetch_assoc($getpostinfo);
$like_post_title = $rowpostinfo['field'];
// Using my "tiny" function to shorten the title if the module is "Quotes"
if($like_module_id == "19"){
Tiny($like_post_title, "75");
$like_post_title = "\"$tiny\"";
}
if(!$like_post_title){
$like_post_title = "<i>Unknown</i>";
}
else {
$like_post_title = "<a href='$post_url'>$like_post_title</a>";
}
echo "<tr class='$altrow'>
<td>$like_post_title</td>
<td align='center'><a href='$mod_url'>$mod_title</a></td>
<td align='center'>$fpg_url</td>
</tr>";
$altrow = ($altrow == 'altrow')?'':'altrow';
} // end while
echo "<tr><Td align='center' colspan='3'>";
// FUNCTIONS - Pagination links
PaginationLinks("$cs_url/users/$profile_id", "likes");
echo "</td></tr></table>";
} // end else if no likes
Ok that may be hard for you to understand since I have alot of my own variables, but basically it gets the module id and post id from the likes table and then runs a query to get the title of the post and any other info I want like the original author.
I have "module" functions set up that will return the url or the title of the module given you provide an id for it.
So if I'm not mistaken, you are trying to create a Favorites table to collect the user's favorite items right? If so, you will need at least two tables.
Types: The types of the resources.
+----+---------+
| ID | Name |
+----+---------+
| 0 | blog |
| 1 | post |
| 2 | article |
| 3 | photo |
| 4 | video |
+----+---------+
Favorites: The most important part of the Favorite system, it's kinda like a relationships map.
+--------+----------+--------------+
| UserID | TargetID | TargetTypeID |
+--------+----------+--------------+
| 941 | 1 | 0 |
| 6 | 935 | 1 |
| 26 | 51 | 4 |
| 7 | 87 | 2 |
+--------+----------+--------------+
Posts: The example posts table, you might also have Blogs or Photos and Albums tables.
+-----+------------------+
| ID | Title |
+-----+------------------+
| 0 | This is my post! |
| 51 | Oh, how are you? |
| 935 | Hello, world! |
+-----+------------------+
Now, the SQL Query might be like this (untested):
-- Get the posts
SELECT p.*
FROM Posts p
LEFT JOIN Favorites f
-- Which are favorited by the user 6
ON f.UserID = 6
-- Also get the type id of the `post`,
-- so we can specify the favorite type of the favorite items
AND f.TargetTypeID = (
SELECT ID
FROM Types
WHERE Name = 'post'
)
-- Make sure we only get the posts which are favorited by the user.
WHERE p.ID = f.TargetID
With the SQL Query above, you can get the favorite posts which is been favorited by the User ID 6.
+-----+------------------+
| ID | Title |
+-----+------------------+
| 935 | Hello, world! |
+-----+------------------+