Drupal: MySQL SELECT all posts belonging to a specific forum - mysql

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

Related

Don't count certain columns in the "LIMIT"

I have a system where there can be "posts", "comments" to the posts, and "comments" to other comments. Think of it as a very simplified facebook comment system.
To back the data, I chose to use one table for both posts and comments, as their structure is pretty much the same:
ID (of comment or post) | TopParentID (if post, same as ID, if comment, ID of post) | DirectParentID (0 if post, either ID of post or ID of parent comment if comment) | Some more fields that are the same for both post and comments
What I'd like to achieve: Select, for example, the first 20 posts. However, with the posts, also select the comments of that post.
I know that this sounds like something where a JOIN with another table would be more optimal than having only one table, but I thought that it would be beneficial to let both posts and comments use the same ID counter, to make it easier to find both direct comments to a post, and comments to a comment of a post.
However, I do not know how to design my query with the above table design; I'd somehow need to select the first x rows where DirectParentID = 0, but also rows where TopParentID = the post id of each selected row.
Should I just change my table design after all?
You could do something like this:
SELECT *
FROM t
WHERE topParentId IN (SELECT id
FROM t
WHERE directParentId = 0
AND id <= 20);
LIMIT can't be used in sub-queries, so you would need a where clause to filter out your parent posts (maybe by date or user or something).
If you want to use limit, you can use a 'self-join' like so:
SELECT t.*
FROM (SELECT *
FROM t
WHERE t.did = 0
LIMIT 20) AS t1
INNER JOIN t
ON t.tid = t1.id;
There isn't anything wrong with your design. Self referencing tables are common. You can consider your table to contain 'comment-able objects' and both comments and posts could be categorized into that. But queries which treat posts and comments as two separate entities may be more difficult/complex to create for a table treating them as a single entity (at least, that's the case for me :))
I recommend you have one table for the "Thread" plus one table for "Comments". Probably "Posts" can simply be thrown with Comments, but do not try to put Thread in also.
If you want a hierarchy (Comments to Comments), the Post should be distinguished from Comments simply by directParentId = 0.
If you don't want a hierarchy, then all you need is a thread_id on all Posts/Comments, plus a simple flag to say which is the original Post.

SQL Join getting like count of a post

I have a table that contains the information of all the posts, table's name is "paylasimlar". and I have another table that contains the information of every like action, table's name is "begeniler". It has 2 columns:
1-User ID of who liked the post
2-The Post ID that liked
The thing I want to do is to write a query with joins that returns all the information of the posts from table "paylasimlar" with the count of likes it got. The problem I ran into is; if a post hasn't got liked yet there would be no information on the "begeniler" table and it would not return the information of that table as a row. Can someone help?
select a.*,count(b.PostID) from paylasimlar a
left join begeniler b on a.PostID and b.PostID group by b.PostID

Tips for building complex SQL queries

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.

MySQL storing and searching string

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.

MySQL: grab one row from each category, but remove duplicate rows posted in multiple categories

I have a database of articles, which are stored in categories. For my homepage, I want to grab an article from each category (I don't care which). However, some articles are crossposted to multiple categories, so they come up twice.
I have a table called tblReview with the article fields (reviewID, headline, reviewText) and a table called tblWebsiteContent that tells the site which categories the articles are in (id, reviewID, categoryID) and finally, a table called tblCategories (categoryID, categoryName) which stores the categories.
My query basically joins these tables and uses GROUP BY tblCategory.categoryID. If I try adding 'tblReview.reviewID' into the GROUP BY statement, I end up with hundreds of articles, rather than 22 (the number of categories I have).
I have a feeling this needs a subquery but my test efforts haven't worked (not sure which query needs to contain my joins / field list / where clause etc).
Thanks!
Matt
SELECT T.categoryName, tR.headline, tR.reviewText
FROM (
SELECT tC.categoryName, MAX(tR1.reviewID) reviewID
FROM tblReview tR1 join tblWebsiteContent tWC on tR1.reviewID = tWC.reviewID
join tblCategory tC on tC.categoryID = tWC.categoryID
GROUP BY tC.categoryName) T JOIN
tblReview.tR on tR.reviewID = T.reviewID
this query will select for each category an article headline corresponding to the Max reviewId for that category (you said 'I don't care which')
Try using SELECT DISTINCT. (This will only work if your SELECT is only pulling the article ID.)
select DISTINCT reviewID