MySQL Query join last comment to topic - php

I have a topic and topic_comments table. I want to join them both.
+------------+ +------------+
| Topic | | Comments |
+------------+ +------------+
| id | | parent_id |
| title | | content |
| createdate | | createdate |
| content | | creator_id |
+------------+ +------------+
The join is on topic.id = topic_comments.parent_id. I want to show the topic with latest comment and order by latest comment createdate. And not show duplicate topics. Can anyone help me?
So far I have this:
select p.id, p.title, p.createdate, p.content, p.int_0 as reacties_total, p.char_1 as prio, p.char_0 as status, r.createdate as r_createdate, r.creator_id as r_creator_id, r.content as r_content
from pages p, topic_reacties r
where r.parent_id = p.id
and p.parent_id = ' . $this->id . '
order by p.int_2 desc
This however doesn 't show topics without comments. It only returns topics with reactions.

Do you want to show topics with the latest comment and sorted by comment or every topic whether it has a comment or not? Those are two different requirements.
If you want to show topics with the latest comment, use the join just as you did (except please lose the archaic syntax), but add more detail to the on clause:
select p.id, p.title, p.createdate, p.content,
p.int_0 as reacties_total, p.char_1 as prio,
p.char_0 as status, r.createdate as r_createdate,
r.creator_id as r_creator_id, r.content as r_content
from pages p
join topic_reacties r
on r.parent_id = p.id
and r.createdate =(
select Max( createdate )
from topic_reacties
where parent_id = r.parent_id )
order by r.createdate;
Don't let the subquery scare you. Test it and you will see impressive efficiency -- if the createdate column is indexed.
If you want all topics whether they have a comment or not but those with comments only the latest one, then use an outer join. You would still use the same subquery but some DBMSs don't allow subqueries in the on clause of outer joins. Oracle for instance. I'm not sure about MySQL. In any event, I always move it to the where clause just to be safe. (I deal with a lot of different DBMSs.)
However, when you place a check of the outer table of an outer join in the where clause, you can convert the output to be the same as an inner join. So you have to use a little trick.
select p.id, p.title, p.createdate, p.content,
p.int_0 as reacties_total, p.char_1 as prio,
p.char_0 as status, r.createdate as r_createdate,
r.creator_id as r_creator_id, r.content as r_content
from pages p
left join topic_reacties r
on r.parent_id = p.id
where (r.parent_id is null
or r.createdate =(
select Max( createdate )
from topic_reacties
where parent_id = r.parent_id ))
order by r.createdate;
Note that I've placed parentheses/brackets around the where check although, as written, they are not absolutely necessary. However, if you add any other check you will need them to go outside the parentheses.
where (r.parent_id is null
or r.createdate =(
select Max( createdate )
from topic_reacties
where parent_id = r.parent_id ))
and p.ID = :SomeID
Check out the SQL Fiddle

SELECT title,content,max(createdate) as createdate
FROM topic
left join comments
on topic.id=comments.parent_id
group by title
order by createdate desc;

Related

MySQL Query On The Same Table Different Columns

I have two tables: Project and ProjectFieldValue
I am needing to return results from the ProjectFieldValue based on multiple different key/value options in the table. I can get it to work with one key/value pair, but once I add another AND statement to the query it returns nothing.
Here is a sample of my tables followed by my query...
Project Table
----------------------
id | name
----------------------
1 | Project #1
ProjectFieldValue Table
I have millions of records like this and they are all stored in this table and associated to a specific Project.
----------------------------------------------------------------------------------------
id | project_id | text_value | date_value | field_key
----------------------------------------------------------------------------------------
1 | 1 | Active | NULL | contract_status
2 | 1 | NULL | 2020-06-02 00:01:58 | listing_date
3 | 1 | Seller | NULL | contract_client_type
4 | 1 | Active | NULL | contract_option
Here are my queries broken down by what works and doesn't work:
This does work, but, it is searching on 1 key/value pair...
SELECT p.name, p.id
FROM ProjectFieldValue pfv
LEFT JOIN Project p
ON pfv.project_id = p.id
WHERE (pfv.text_value IN ( SELECT text_value FROM ProjectFieldValue WHERE text_value IN ('Active')) AND field_key = 'contract_status')
GROUP BY p.id
This doesn't work because it is searching on 3 key/value pairs...
SELECT p.name, p.id
FROM ProjectFieldValue pfv
LEFT JOIN Project p
ON pfv.project_id = p.id
WHERE (pfv.text_value IN ( SELECT text_value FROM ProjectFieldValue WHERE text_value IN ('Active')) AND field_key = 'contract_status')
AND (pfv.text_value IN ( SELECT text_value FROM ProjectFieldValue WHERE text_value IN ('Seller')) AND field_key = 'contract_client_type')
AND (pfv.date_value between '2020-07-08 00:00:00' AND '2020-07-11 23:59:59' AND pfv.field_key = 'listing_date')
GROUP BY p.id
Goal
Ultimately, what I would need to be able to do is search on unlimited key/value pairs in this table and return all results grouped by the p.id
Thanks for your help!
This should do the thing.
SELECT p.name, p.id
FROM ProjectFieldValue pfv
LEFT JOIN Project p
ON pfv.project_id = p.id
WHERE (field_key = 'contract_status' AND pfv.text_value = 'Active')
OR (field_key = 'contract_client_type' AND pfv.text_value = 'Seller')
OR (pfv.field_key = 'listing_date' AND pfv.date_value between '2020-07-08 00:00:00' AND '2020-07-11 23:59:59')
GROUP BY p.id;
But I have doubt why you are Left joining Project and ProjectFieldValue. A simple Inner Join should solve your purpose. As you are grouping by on p.id. You may encourage a lot of NULLed columns. So I would suggest below.
SELECT p.name, p.id
FROM Project p
JOIN ProjectFieldValue pfv
ON p.id = pfv.project_id
WHERE (field_key = 'contract_status' AND pfv.text_value = 'Active')
OR (field_key = 'contract_client_type' AND pfv.text_value = 'Seller')
OR (pfv.field_key = 'listing_date' AND pfv.date_value between '2020-07-08 00:00:00' AND '2020-07-11 23:59:59')
GROUP BY p.id;

COUNT number of rows on table with LEFT JOIN returns 1 when not exists

I've two tables, for example:
post:
id | author | content | date
1 | Lucas | Hello! | 2016
2 | Igor | Hi! | 2016
comment:
id | post_id | content | date
1 | 2 | hehehe | 2016
2 | 1 | hahaha | 2016
3 | 2 | huhuhu | 2016
And I to do a SELECT that return all posts and a COUNT of rows of all comments with post.id = comment.id.
So, I tried:
SELECT p.id, p.author, p.content, p.date, COUNT(*) AS numComments FROM post p LEFT JOIN comment ON p.id = post_id WHERE p.author = '$author' GROUP BY p.id DESC LIMIT 12
And I got do it. But, even when no exists comments with p.id = post_id he returns 1.
So, I tried:
SELECT p.id, p.author, p.content, p.date, CASE WHEN COUNT(*) < 1 THEN '0' ELSE COUNT(*) END AS numComments FROM post p LEFT JOIN comment ON p.id = post_id WHERE p.author = '$author' GROUP BY p.id DESC LIMIT 12
But the result is the same. How to do this?
As outer joins return a row even if there's no matching data you need to count a column from the inner table, usually it's the column used in join:
SELECT p.id, p.author, p.content, p.date, COUNT(post_id) AS numComments
FROM post p LEFT JOIN comment ON p.id = post_id
WHERE p.author = '$author'
GROUP BY p.id -- seems to be mysql, otherwise you need to add more columns to the list
If you don't want to show rows with a zero count simply switch to an
INNER JOIN.
you can get count by this way, also last is order by not group by:
SELECT p.id, p.author, p.content, p.date,
(select COUNT(*) from comment where p.id = comment.post_id) AS numComments FROM post p
WHERE p.author = '$author'
ORDER BY p.id DESC LIMIT 12

INNER JOIN on three tables with condition

I am trying to fetch all the photos detail from a tag slug (URL for the tag), the database has three tables:
|-----------------------|
|==> photo |
| -> id |
| -> custom_id |
| -> title |
|-----------------------|
|==> tags |
| -> id |
| -> slug |
|-----------------------|
|==> tags_relation |
| -> tid | <-- this is the tags.id
| -> pid | <-- this is the photo.custom_id
|-----------------------|
here is my mysql code to INNER JOIN all the tables and to get 20 photos from a tag:
SELECT photo.*, tags.*, tags_relation.*,
FROM tags WHERE tags.slug = 'people'
INNER JOIN tags_relation ON = tags_relation.tid = tags.id
INNER JOIN photo ON photo.custom_id = tags_relation.pid
LIMIT 20
ORDER BY photo.date DESC
The query is not correct anyway, and i can't understand how the INNER JOIN should work here, any idea?
Thanks
SQL has a specific ordering of clauses. In your case:
SELECT
FROM
WHERE
GROUP BY
ORDER BY
LIMIT
This is always the ordering within a query. Note that JOIN expressions are not "clauses". They are part of the FROM clause (and in MySQL, the update and delete clauses as well).
Applied to your query:
SELECT p.*, t.*, tr.*
FROM tags t INNER JOIN
tags_relation tr
ON tr.tid = t.id INNER JOIN
photo p
ON p.custom_id = tr.pid
WHERE t.slug = 'people'
ORDER BY p.date DESC
LIMIT 20
You will note that the indentation highlights the clauses which are a fundamental part of the language.
I also added table aliases, which make the query easier to write and to read. And fixed some minor things, such as misplaced commas.
I note you are pulling too many columns out of the data. You should just list the columns you want (probably p.*).
try this..
SELECT photo.*, tags.*, tags_relation.*
FROM tags WHERE tags.slug = 'people'
INNER JOIN tags_relation ON(tags_relation.tid = tags.id)
INNER JOIN photo ON (photo.custom_id = tags_relation.pid)
ORDER BY photo.date DESC
LIMIT 20

How to order by COUNT with SUM and MINUS of multi tables

I'm trying to show post by order them with sum of comment and like.
There are three table using in this query post,comment and like
for table like it has column type that keep value like or unlike.
SQL
SELECT (SELECT COUNT(id) AS count_comment
FROM comment WHERE comment.post_id = post.post_id),
(SELECT COUNT(id) AS count_like
FROM like WHERE like.post_id = post.post_id AND like.type = 'like'),
(SELECT COUNT(id) AS count_unlike
FROM like WHERE like.post_id = post.post_id AND like.type = 'unlike'),
post.* FROM post
ORDER BY (count_comment + count_like - count_unlike) DESC;
So, this is an example when it shows on the page
post_id | comment | like | unlike | (comment+like-unlike)
4 | 5 | 3 | 1 | 7
1 | 2 | 3 | 0 | 5
2 | 1 | 1 | 4 | -2
... | ... | ... | ... | ...
My problem is my SQL is very slow, please suggest another way if it can. I've tried to use JOIN but i can't figured out how its SQL should be, please help thanks.
Using a derived table for each of the counts, the query below counts comments, likes, unlikes for each post and then joins the counts to the post table by post_id.
SELECT
p.post_id,
COALESCE(c.comment_count,0) comment_count,
COALESCE(l.like_count,0) like_count,
COALESCE(ul.unlike_count,0) unlike_count,
(COALESCE(c.comment_count,0)
+ COALESCE(l.like_count,0)
- COALESCE(ul.unlike_count,0)) total
FROM post p
LEFT JOIN (
SELECT c.post_id,
COUNT(*) comment_count
FROM comment c
GROUP BY c.post_id
) c ON c.post_id = p.post_id
LEFT JOIN (
SELECT l.post_id,
COUNT(*) like_count
FROM like l
WHERE l.type = 'like'
GROUP BY l.post_id
) l ON l.post_id = p.post_id
LEFT JOIN (
SELECT ul.post_id,
COUNT(*) unlike_count
FROM like ul
WHERE ul.type = 'unlike'
GROUP BY ul.post_id
) ul ON ul.post_id = p.post_id
ORDER BY total DESC

get all comments including its votes for a post

I have following three tables in mysql.
postid | post_content => posts
commentid | post | comment_content => comments
commentvoteid | comment | voter => comment_votes
I want to fetch all the comments, counting its votes for a post.
commentid | comment_content | comment_votes => my_result
I have tried the following query but not getting the desired result.
SELECT commentid,comment_content,count_comments.total AS comment_votes
FROM comments
INNER JOIN(SELECT COUNT(*) AS total FROM comment_votes WHERE comment=comments.commentid) AS count_comments
WHERE post={$postId}
Is it possible to fetch the result as I wanted? How can I do that?
You can use GROUP BY to achieve what you want:
SELECT commentid,comment_content,COUNT(*) AS total
FROM comments
INNER JOIN comment_votes ON (comment_votes.comment=comments.commentid)
WHERE post={$postId}
GROUP BY commentid;
The method that you are trying uses a correlated subquery. You can do this, but the correlated subquery needs to go into the select clause:
SELECT c.commentid, c.comment_content,
(SELECT COUNT(*) FROM comment_votes cv WHERE cv.comment = c.commentid
) AS comment_votes
FROM comments c
WHERE post={$postId};
Normally, I much prefer the group by approach but sometimes in MySQL this can be faster.
maybe like this:
select a.commentid, a.comment_content, count(b.*) as total from (
select commentid, comment_content from comments where post={$postId}) a
join comment_votes b on b.comment = a.commentid
group by a.commentid, a.comment_content;

Categories