Merging two select queries. Make them unique - mysql

Could someone hint me in the direction of how to merge these two mysql queries?
As you see there is a bug with the distinct too. I need the topicids without duplicates and the date for the post from which the topicid comes.
I added the distinct just to get you an idea of what i need. After getting the ids there comes the second query which reads the topicdetails...
I am stuck on this.
Get the unique topicid from the last n,15 posts for a category, but with the date:
SELECT distinct( p.topicid ), p.date as lastaction
FROM posts p
WHERE p.category = ?
ORDER BY p.id
DESC
LIMIT
n, 15
SELECT t.id, t.title, t.date, t.state
FROM topics t
WHERE t.id in( $var_ids )
thanks.

SELECT DISTINCT
p.[topicid],
p.[date] AS lastaction,
t.[id],
t.[title],
t.[date],
t.[state]
FROM posts p
INNER JOIN topics t
ON p.topicid = t.id
WHERE p.category = /* Enter Category */ AND t.id in ( $var_ids )
ORDER BY p.topicid DESC
Use this as a starter for 10. I'm unsure what 'n' is (e.g Column?) so i've left this out

Related

How to count only certain rows in a GROUP BY in Rails

In the application I'm building, there are posts and tags, and they are connected through a many-to-many relation. What I want to do is show all tags to the user and sort them by how many published posts they have (which is determined by the is_published column in the posts table).
Right now I'm sorting them by how many posts they have in general (both published and unpublished) with this code:
scope :top_used, -> { left_joins(:posts).group(:id).order("COUNT(posts.id) DESC") }
Which translates to the MySQL:
SELECT t.*
FROM tags t
LEFT
JOIN post_tags pt
ON pt.tag_id = tags.id
LEFT
JOIN posts p
ON p.id = pt.post_id
GROUP
BY t.id
ORDER
BY COUNT(p.id) DESC
So, again, I need to sort by the count of the number of published posts not all posts. Can this be done in MySQL?
Maybe this:
SELECT `tags`.* FROM `tags`
LEFT OUTER JOIN `post_tags` ON `post_tags`.`tag_id` = `tags`.`id`
LEFT OUTER JOIN `posts` ON `posts`.`id` = `post_tags`.`post_id` AND posts.is_published = 1
GROUP BY `tags`.`id`
ORDER BY COUNT(posts.id) DESC
?
Assuming that the column is_published's data type is Boolean or Integer, with values 1 or 0 you can order by the sum of the values:
ORDER BY SUM(posts.is_published) DESC
If is_published is nullable, use COALESCE():
ORDER BY COALESCE(SUM(posts.is_published), 0) DESC

How to properly join these three tables in SQL?

I'm currently creating a small application where users can post a text which can be commented and the post can also be voted (+1 or -1).
This is my database:
Now I want to select all information of all posts with status = 1 plus two extra columns: One column containing the count of comments and one column containing the sum (I call it score) of all votes.
I currently use the following query, which correctly adds the count of the comments:
SELECT *, COUNT(comments.fk_commented_post) as comments
FROM posts
LEFT JOIN comments
ON posts.id_post = comments.fk_commented_post
AND comments.status = 1
WHERE posts.status = 1
GROUP BY posts.id_post
Then I tried to additionally add the sum of the votes, using the following query:
SELECT *, COUNT(comments.fk_commented_post) as comments, SUM(votes_posts.type) as score
FROM posts
LEFT JOIN comments
ON posts.id_post = comments.fk_commented_post
AND comments.status = 1
LEFT JOIN votes_posts
ON posts.id_post = votes_posts.fk_voted_post
WHERE posts.status = 1
GROUP BY posts.id_post
The result is no longer correct for either the votes or the comments. Somehow some of the values seem to be getting multiplied...
This is probably simpler using correlated subqueries:
select p.*,
(select count(*)
from comments c
where c.fk_commented_post = p.id_post and c.status = 1
) as num_comments,
(select sum(vp.type)
from votes_posts vp
where c.fk_voted_post = p.id_post
) as num_score
from posts p
where p.status = 1;
The problem with join is that the counts get messed up because the two other tables are not related to each tother -- so you get a Cartesian product.
You want to join comments counts and votes counts to the posts. So, aggregate to get the counts, then join.
select
p.*,
coalesce(c.cnt, 0) as comments,
coalesce(v.cnt, 0) as votes
from posts p
left join
(
select fk_commented_post as id_post, count(*) as cnt
from comments
where status = 1
group by fk_commented_post
) c on c.id_post = p.id_post
left join
(
select fk_voted_post as id_post, count(*) as cnt
from votes_posts
group by fk_voted_post
) v on v.id_post = p.id_post
where p.status = 1
order by p.id_post;

Duplicated rows

SQL Query:
SELECT
T.*,
U.nick AS author_nick,
P.id AS post_id,
P.name AS post_name,
P.author AS post_author_id,
P.date AS post_date,
U2.nick AS post_author
FROM
zero_topics T
LEFT JOIN
zero_posts P
ON
T.id = P.topic_id
LEFT JOIN
zero_players U
ON
T.author = U.uuid
LEFT JOIN
zero_players U2
ON
P.author = U2.uuid
ORDER BY
CASE
WHEN P.date is null THEN T.date
ELSE P.date
END DESC
Output:
Topics:
Posts:
Question: Why i have duplicated topic id 22? i have in mysql two topics (id 22 and 23) and two posts(id 24 and 25). I want to see topic with last post only.
If a join produces multiple results and you want only at most one result, you have to rewrite the join and/or filtering criteria to provide that result. If you want only the latest result of all the results, it's doable and reasonably easy once you use it a few times.
select a.Data, b.Data
from Table1 a
left join Table2 b
on b.JoinValue = a.JoinValue
and b.DateField =(
select Max( DateField )
from Table2
where JoinValue = b.JoinValue );
The correlated subquery pulls out the one date that is the highest (most recent) value of all the joinable candidates. That then becomes the row that takes part in the join -- or, of course, nothing if there are no candidates at all. This is a pattern I use quite a lot.

How to order by one-to-many association date with a decent performance?

I have the above models in my MySQL database:
Blogs (id: integer, name: varchar)
Posts (id: integer, name: varchar, blog_id: integer, created_at: date)
I want to retrieve a list of all the blogs, ordered by the ones that have the newests posts.
I've reached that with the following query:
SELECT b.*, (SELECT p.created_at FROM posts p WHERE p.blog_id = b.id ORDER BY p.created_at DESC LIMIT 1) AS last_post_created_at FROM blogs b ORDER BY last_post_created_at DESC;
But this query is too slow and I'm unable to use it on my application.
Do you guys have a good solution for that?
Thank you.
A rewriting of the query:
SELECT b.*,
p.last_post_created_at
FROM blogs b
LEFT JOIN
( SELECT blog_id,
MAX(created_at) AS last_post_created_at
FROM posts
GROUP BY blog_id
) AS p
ON p.blog_id = b.id
ORDER BY last_post_created_at DESC;
An index on (blog_id, created_at) will help both this and your version.
If you want to limit the number of blogs returned, you should add the ORDER BY in the subquery and put the LIMIT there:
SELECT b.*,
p.last_post_created_at
FROM blogs b
LEFT JOIN
( SELECT blog_id,
MAX(created_at) AS last_post_created_at
FROM posts
GROUP BY blog_id
ORDER BY last_post_created_at DESC
LIMIT 100
) AS p
ON p.blog_id = b.id
ORDER BY last_post_created_at DESC;
Use a view. Great thing to know and use!

group by on a left join'd column

Here is my query, it does group them by postid but it will not group by the project id or the pid
SELECT
tracker.postid as postid,
sum(tracker.clicks) as clicks,
projects.id as pid
FROM
tracker_dailyclicks tracker
LEFT JOIN posts
ON tracker.postid = posts.id
LEFT JOIN projects
ON projects.id = posts.project_id
WHERE
tracker.date >= '2012-03-30'
AND tracker.date <= '2012-03-30'
GROUP BY
postid,
pid
ORDER BY
clicks DESC
I'm not sure where to go from here, I think it has something to do with trying to group by a column that was joined from another join. Just looking for some insight.
SELECT tracker.postid as postid, sum(tracker.clicks) as clicks, projects.id as pid
FROM `tracker_dailyclicks` tracker
LEFT JOIN posts ON posts.id = postid
LEFT JOIN projects ON projects.id = posts.project_id
WHERE tracker.date >= '2012-03-30' AND tracker.date <= '2012-03-30'
GROUP BY tracker.postid, projects.id ORDER BY clicks DESC
I believer the problem you are running into is that it is a LEFT JOIN and is probably hitting NULL values. So, whenever you have NULL values, you always can get strange side effects... I would change to...
SELECT ...
COALESCE( projects.id, 0 ) as pid
Since you are already using your "pid" column alias as the group by, should have no impact there.