I have started programming for sometime with PHP using MySQL as the database and I know basic SQL queries upto simple JOINS containing upto two tables.
But the moment I need to get results from 3 or more tables, I am stuck. No matter how hard I try, I still manage to find myself lost. I have searched everywhere looking for a good tutorial on how to tackle complex SQL queries but haven't found anything that explains how to go about it. Most tutorials consists of solutions for a particular problem, but they don't explain the perfect general procedure how to go about tackling a problem
Can anyone explain the basic general way of going about from start to finish, how to construct the query, etc when it comes to complex queries.
For example:
I have a forum with the following database structure:
forumCategory:
id | name | desc
forumTopic:
id | category_id | created_on | created_by | title | content
forumPost:
id | topic_id | created_on | created_by
users:
id | first_name | last_name
All topics are created in the forumTopic table. All replies to that topic are inserted in the forumPost table.
Now on the forum main page, I need to display the categories, the very last post posted by a user in that particular category, the user who posted the last post.
The flow I thought of was :
Find the last post in a category by looking at the MAX(id) in the forumPost table grouped by topic_id. This will get me the ID of the last post in every topic.
Now again find the MAX(of the IDS I got earlier) grouped by category_id. This will get me the last post in every category.
IDs are autoincrementing primary keys.
But I got stuck with constructing a SQL query from the above algorithm
Would be very helpful if someone could help me out on this one.
Get the last post for each category by joining Post to Topic
SELECT category_id , category.name, Max(ForumPost.ID) as maxpostid
from ForumPost
inner join ForumTopic on ForumPost.Topic_ID = ForumTopic.ID
inner join ForumCategory on ForumTopic.Category_Id = ForumCategory.ID
group by category_Id, category.name
(This is an intermediate stage for explanatory purposes - it is included in the query below )
Then join this to the user table to find out the user name (presumably a post has a userid?)
select users.name, lastposts.*
from
forumpost
inner join
(
SELECT category_id , category.name, Max(ForumPost.ID) as maxpostid
from ForumPost
inner join ForumTopic on ForumPost.Topic_ID = ForumTopic.ID
inner join ForumCategory on ForumTopic.Category_Id = ForumCategory.ID
group by category_Id, category.name
) lastposts
on forumpost.id = lastposts.maxpostid
inner join
users on forumpost.userid =users.id
However, you may want to consider updating the category table with the last post each time a post is made. That way you can run a much simpler query for your forum front page.
Related
I need to select all the messages (first posts and replies) posted in an array of specific categories (forums) in Drupal.
First posts are stored in field_data_body, replies are stored in field_data_comment_body.
The structure of field_data_body and field_data_comment_body is the same, in the column body_value there's the content of the posts and in the column entity_id their unique ID.
The table field_data_taxonomy_forums contains the entity_id column and the taxonomy_forums_tid column (which are the IDs of the forum categories). The table taxonomy_term_data contains the columns tid (which is the same of taxonomy_forums_tid and the description column (which is the title of the forum category).
So, I'm looking for a query that allows me to select the body of the posts (both first posts and replies) and the description of the forum specifying an array of tids (i.e. the IDs of the forum categories), that I'll manually find in the taxonomy_term_data table.
So, for example I'm looking for the query that allows me to SELECT the posts "belonging" to tids 1456,7622,862 and the relative tid description.
Here's a screenshot of the field_data_body table :
I think, it would be better for us to split the task into 2 subtasks:
Find the bodies of the posts, belonging to a particular tid.
Find all the comments of the body, belonging to a particular tid.
We'll need to use these tables:
field_data_taxonomy_forums
field_data_body
comment
field_data_comment_body
Database structure:
Finding the bodies
SELECT
taxonomy_forums.taxonomy_forums_tid AS tid,
body.entity_id,
body.body_value AS body
FROM
field_data_taxonomy_forums AS taxonomy_forums
INNER JOIN
field_data_body AS body
ON
body.entity_id=taxonomy_forums.entity_id
WHERE
taxonomy_forums.taxonomy_forums_tid IN (9);
Finding the comments for the bodies
Here we'll need comments table, that unites field_data_body and field_data_comment_body.
SELECT
taxonomy_forums.taxonomy_forums_tid AS tid,
comment_body.entity_id,
comment_body.comment_body_value AS body
FROM
field_data_taxonomy_forums AS taxonomy_forums
INNER JOIN
field_data_body AS body
ON
body.entity_id=taxonomy_forums.entity_id
INNER JOIN
comment
ON
comment.nid=body.entity_id
INNER JOIN
field_data_comment_body AS comment_body
ON
comment_body.entity_id=comment.cid
WHERE
taxonomy_forums.taxonomy_forums_tid IN (9);
If you UNION these 2 queries, you'll get the list of posts and comments.
sqlfiddle
To find reference to forum id you must add use of "forum" table (table establishing relationship of nodes to forum terms.)
Also it should be noted that forum id does not exist, it is a taxonomy id.
Here is an example to get only first message topic by taxonomy id(tid)
SELECT * FROM field_data_body fdb
LEFT JOIN forum f ON f.nid = fdb.entity_id
WHERE fdb.bundle="forum" AND f.tid=15
Note that the forum module works with comment core module wich means that only first topic message is stored in "field_data_body" and all replies are stored in "field_data_comment_body" table.
Hope it helps
I am having a database design issue and i'm still pretty new to MySQL so I thought I would ask here. What would be the best way to get data for a chronological feed from multiple tables? For example a user does many things, they vote, comment, rate, ask questions. I save all this information in their respective tables "tblVote", "tblRate" etc, now the tricky part. a user can follow a user or many, so say you follow 3-4 people. Following allows you to see their interactions, voting, rating, commenting, asking questions etc in your feed (like facebook or something similar).
What would be the best way to get all the information from all 5 tables for every person they follow and then sort all of that chronologically? I Am assuming my current method (foreach follower grab all votes, comments, ratings etc and sort all would be terrible)
My working theory, so my working idea is to create a Interaction table, that has a column for the users id, the id of the other tables entry, and a type reference. so for example
User ID | InteractionID | Type
9 1232 Comment
10 80 Rating
9 572 Vote
Then you could just go ahead and grab all Interactions for each of the people they follow, sort that and then say grab the top 10? and query the individual databases to get the full info (time of comment, text of comment etc)
A many to many relationship exists between User and Follower. Since, Follower is also another user, this becomes a recursive many-to-many. When you decompose this relationship, you get a Association table or a gerund.
User_Follower {id, userid_fk, followerid_fk}
Both the userid_fk and followerid_fk are referencing to the User table.
Now, I am assuming you have a One-to-many relationship between User-tblRate, User-tblVote, User-tblPost etc.
So, you can write a join something like this:
select p.postTitle, p.postTag, ...,
c.commentId, c.commentData, ...
from (tblUser u INNER JOIN tblPost p
ON (p.userid = u.userid)) INNER JOIN tblComment c
ON (c.userid =
u.userid)
where u.userid in
(select userid_fk from user_follower where followerid_fk = 100)
orderby p.datetime_col ASC, c.datetime_col ASC
LIMIT 10;
100 is the user you want to get the information for.
The idea is that you just have one association table linking the User and Follower, then use simple joins to get the data for all the followees
I have four tables
post
-------------
post_id
cat_id
posts
post_category
-------------
cat_id
cat_name
users
-------------
user_id
user_name
user_category_map
-------------
user_id
cat_id
I want all posts added by all users in all post categories
I have written this query
SELECT posts
FROM post p, users u, user_category_map ucm
WHERE p.cat_id = ucm.cat_id
AND ucm.user_id = u.user_id
but I am getting repeated posts. Is my table structure correct and normalized properly. I am not able to correctly grab the logic. Is the join I put is correct?
Here you received the full cartesian product of all tables, and filter it by " p.cat_id = ucm. cat_id and ucm.user_id = u.user_id" condition.
Query like this typically converted to optimized join versions - merge, nested loop, index or hash join.
During cartesian product A*B*C every row from A will be repeat B*C times.
Your query "all posts added by all users in all post categories" is
select * from post
The design does not look correct. Instead of a reference table for user_category, you should have a reference table for user_post. In current scenario, if same user adds multiple posts for same category, you will end up having duplicate rows in user_category_map
This might not be possible but would be amazingly awesome if it were. I have the following basic table structure:
groups
group_id
other_stuff
users
user_id
other_stuff
users_to_groups
user_id
group_id
other_stuff
events
event_id
group_id (where the event belongs)
other_stuff
events is actually a set of tables for the various actions a user can perform on the site.
I would like to be able to perform a query on of the tables and have it return a result something like:
event_type event_id info_columns ...
user_join user_to_group_id
photo_upload photo_id
comment comment_id
where the value in the event_type column would be one generated by the query based on the table name of the source content.
I know I can do this using multiple queries and then piecing them together in PHP, but I was thinking that maybe there is a way to do it entirely in MySQL. Is something like this even possible? If so, what are the basic steps to make it happen?
if you have a number of select statements, and you get the data you want in each, then of course you can join them.
Others have asked similar questions, mysql-join-most-recent-matching-record-from-one-table-to-another
Now, given you're only asking for the latest event. your pseudo code goes
select <userinfo>
from users
join groups
on user=group
join (select lastest event by group
from events
) as tmp
on group=tmp
That way you do 1 query, you hand off that work to the database
I’m currently in the process of developing my own blogging system. Currently when you create a new post, you get the option to archive it categories of your own choise.
Currently I’m storing the categories as a VARCHAR value in a mysql database. As an example the field will contain 2,4,8 if the user has chosen the categories with ID: 2, 4 and 8.
To retrieve the blog posts for the category with ID 4 I then use:
SELECT col FROM table WHERE LOCATE(',4,', CONCAT(',',col,','))
I’ve been told that values seperated with a decimal comma is a no-go (very bad) when it comes to good database structure!
Can anyone provide me with a good way/technique to make this the most effective way?
Thanks in advance
A flexible & robust setup, as posted so many times in SO:
POSTS
id
name
text
CATEGORIES
id
name
POST_CATEGORIES
post_id
category_id
Where the current query would be:
SELECT p.id, p.name, p.text
FROM posts p
JOIN post_categories pc
ON pc.post_id = p.id
AND pc.category_id = 4;
Look into relational database normalization. For your specific case consider creating 2 additional tables, Categories and BlogCategories in addition to your Blog content table. Categories contain the definition of all tags/categories and nothing else. The BlogCategories table is a many-to-many cross reference table that probably in your case just contains the foreign key reference to the Blog table and the foreign key reference to the Categories table. This allows 1 Blog entry to be associated with multiple categories and 1 Category to be associated with multiple Blog entries.
Getting the data out won't be any more difficult than a 3 table join at worst and you'll be out of the substring business to figure our your business logic.