Sorting relational tables using only 1 field in mysql - mysql

I have 2 relational tables and I joined them, and I want it to be sorted by the same field.
Tables:
posts fields: id, post, created
reposts fields: id,post_id, created
Here's what I currently have:
SELECT
p.post as post,
p.created as post_created,
rp.created as repost_created
FROM posts p
LEFT JOIN reposts rp
ON rp.post_id = p.id
ORDER BY rp.created DESC, p.created DESC
And here's the result of that code:
{1
But what I want is to sort the whole data, base on the created field as a whole, regarding if it has repost_created or if it's null, it will base on the post_created field. Like this:
[2
Thank you very much!

It looks like you just need to COALESCE the value of rp.created with p.created and sort on that; that way if rp.created is not NULL you'll sort on that date, otherwise you'll sort on p.created:
ORDER BY COALESCE(rp.created, p.created) DESC
Note that you may want an additional criterion to break ties (when two rows have the same datetime value), you could perhaps use
ORDER BY COALESCE(rp.created, p.created) DESC, rp.id IS NULL
this will sort reposts ahead of posts at the same time.

i think your purpose is
SELECT
p.post as post,
p.created as post_created,
rp.created as repost_created
FROM posts p
LEFT JOIN reposts rp
ON rp.post_id = p.id
ORDER BY isnull( p.created ,rp.created)

Related

Helpful answer as the second post - sort order asc

A forum has topics and in this topics are the posts. The sort order is from old to new. It is possible to rate each post with "helpful".
A default SQL selection looks like this:
SELECT * FROM `posts` WHERE `topic_id` = 5033 ORDER BY `post_id` ASC
The "helpful" field in the posts table has the name "post_helpful".
Is it possible to order the posts in this way:
First post - Contains the question
If a post with more then 3 "post_helpful" exists, display this post as the second post. But only the post with the highest score.
Normal post row without the second post id
I only want the post with the highest score on the second post position. But only if the post has more than 3 rates. If there is no post with more than 3 rates, keep the default order
Thank you
Yes. The first part is a little tricky. You can use multiple expressions in the ORDDER BY:
SELECT p.*
FROM posts p CROSS JOIN
(SELECT MIN(p.post_id) as min_post_id
FROM posts p
WHERE p.topic_id = 5033
) pp
WHERE p.topic_id = 5033
ORDER BY (p.post_id = pp.min_post_id) DESC, -- lowest id first
(case when p.post_helpful > 3 then p.post_helpful else 0 end) DESC, -- helpful next
p.post_id ASC;
EDIT:
To get the posts with the maximum helpful:
SELECT p.*
FROM posts p CROSS JOIN
(SELECT MIN(p.post_id) as min_post_id,
MAX(p.post_helpful) as max_post_helpful
FROM posts p
WHERE p.topic_id = 5033
) pp
WHERE p.topic_id. = 5033
ORDER BY (p.post_id = pp.min_post_id) DESC, -- lowest id first
(pp.max_post_helpful > 3 AND p.post_helpful = pp.max_post_helpful) DESC, -- helpful next
p.post_id ASC;
I believe you want to have more complex sorting for the query
Can you assign a value or weight by the count of rows that were flagged helpful
This would then have a number to sort by
I.e. Query 2 counts the number of rows that show the post was helpful
Query 1 is the main query ordered by the value/count from Query 2

Avoiding the “n+1 selects” problem when I want a subset of related resources

Imagine I'm designing a multi-user blog and I have user, post, and comment tables with the obvious meanings. On the main page, I want to show the ten most recent posts along with all their related comments.
The naive approach would be to SELECT the ten most recent posts, probably JOINed with the users that wrote them. And then I can loop through them to SELECT the comments, again, probably JOINed with the users that wrote them. This would require 11 selects: 1 for the posts and 10 for their comments, hence the name of the famous anti-pattern: n+1 selects.
The usual advice for avoiding this anti-pattern is to use the IDs from the first query to fetch all related comments in a second query which may look something like this:
SELECT
*
FROM
comments
WHERE
post_id IN (/* A comma separated list of post IDs returned from the first query */)
As long as that comma separated list is in reasonably short we managed to fetch all the data we need by issuing only two SELECT queries instead of eleven. Great.
But what if I only want the top three comments for each post? I didn't try but I can probably come up with some LEFT JOIN trickery to fetch the most recent posts along with their top three comments in a single query but I'm not sure it would be scalable. What if I want the top hundred comments which would exceed the join limit of 61 tables of a typical MySQL installation for instance?
What is the usual solution for this other than reverting to n+1 selects anti-pattern? What is the most efficient way to fetch items with a subset of items related to each one in this fairly typical scenario?
It is usually a better option to run as few queries as possible, and then implement some application logic on top of it if needed. In your use case, I would build a query that returns both the most recent posts and the most recent associated comments, with proper ordering to make the application processing easier. Then your application can take care of displaying them.
Assuming that you use MySQL (since you mentionned it in your question), let's start with a query that gives you the 10 most recent posts:
SELECT * FROM posts ORDER BY post_date DESC LIMIT 10
Then you can join this with the corresponding comments:
SELECT
p.*,
c.*
FROM
(SELECT * FROM posts ORDER BY post_date DESC LIMIT 10) p
INNER JOIN comments c ON c.post_id = p.id
Finally, let's set up a limit on the number of comments per posts. For this, you can use ROW_NUMBER() (available in MySQL 8.0) to rank the comments per post, and then filter only the a given number of comments. This gives you the 10 most recent posts along with each of their 3 most recents comments:
SELECT *
FROM (
SELECT
p.*,
c.*,
ROW_NUMBER() OVER(PARTITION BY p.post_id ORDER BY c.comment_date DESC) rn
FROM
(SELECT * FROM posts ORDER BY post_date DESC LIMIT 10) p
INNER JOIN comments c ON c.post_id = p.id
) x
WHERE rn <= 3
ORDER BY p.post_date DESC, c.comment_date DESC
Query results are ordered by post, then by comment date. So when your application fetches the resuts, you get 1 to 3 records per post, in sequence.
If you want the last 10 posts
SELECT p.post_id
FROM post p
ORDER BY p.publish_date DESC
LIMIT 10
Now if you want the comment of those posts:
SELECT c.comment_id, u.name
FROM comments c
JOIN users u
on c.user_id = u.user_id
WHERE c.post_id IN ( SELECT p.post_id
FROM post p
ORDER BY p.publish_date DESC
LIMIT 10 )
Now for the last 3 comments is where rdbms version is important so you can use row_number or not:
SELECT *
FROM (
SELECT c.comment_id, u.name,
row_number() over (partition by c.post_id order by c.comment_date DESC) as rn
FROM comments c
JOIN users u
on c.user_id = u.user_id
WHERE c.post_id IN ( SELECT p.post_id
FROM post p
ORDER BY p.publish_date DESC
LIMIT 10 )
) x
WHERE x.rn <= 3
You can do this in one query:
select . . . -- whatever columns you want here
from (select p.*
from posts p
order by <datecol> desc
fetch first 10 rows only
) p join
users u
on p.user_id = u.user_id join
comments c
on c.post_id = p.post_id;
This returns the posts/users/comments in one table, mixing the columns. But it only requires one query.

How can I find records that contain multiple category IDs (using Toxi structure)?

SQL
SELECT *
FROM posts
LEFT JOIN taxonomy_term_map
ON (posts.ID = taxonomy_term_map.object_id)
WHERE taxonomy_term_map.term_id
IN (98,119)
GROUP BY posts.ID DESC
HAVING COUNT(posts.ID ) >= 2
LIMIT 0,20
TABLES & COLUMNS
posts { ID, post_title, etc... }
taxonomy_terms { term_id, term_label, term_slug, etc. }
post_taxonomy_term_map { map_id, object_id, taxonomy, term_id}
(NOTE: object_id relates to the posts.ID value)
My site uses the Toxi taxonomy structure for tagging/categorizing posts. Each post can have multiple term IDs attached to it.
Each taxonomy term associated with a post gets a record in the "post_taxonomy_term_map" table.
The query I'm current using returns matching records at the top of the results ("GROUP BY posts.ID DESC") along with additional records that don't fully match all the terms provided.
I only want to select records that match ALL of the term ID values provided, everything else should be ignored. Additionally, I want to order records by posts.rank, but I can't currently do that while ordering posts by GROUP.
I would appreciate some assistance.
I think this query will get what you need:
select p.* -- get only the columns from posts
from posts p
join post_taxonomy_term_map m on m.object_id = p.id
where m.term_id in (98, 119) -- filter to specific terms
group by p.id
having count(*) = 2 -- filter out incomplete ones
order by p.rank -- order by rank
limit 20 -- get only the first 20 rows

query to have all itms from one table but order by another table that doesn't have all items

I have one table products with id of product and name.
Second table is products_last_usage where I keep product_id, user_id and last_used_at.
Whenever a user clicks on a product, I have the field last_used_at updated.
Now, I need a query to list all products, and order them first by last_used_at, and then by name of product. It has to be PER USER. i.e. every user will have his own order of the table.
But I need all products, even if there are no records of them in the second table.
How to do that?
You can help me with a rails query or mysql query.
You can use a left join:
select p.*
from products p left join
products_last_usage plu
on plu.product_id = p.id and plu.user_id = $user_id
order by (last_used_at is not null) desc, last_used_at desc;
Ordering by last_used_at desc should also work. However, I think it is clearer to explicitly handle NULL values.
I think you can start from something like this. Pls next time post sample data, expected results etc.
SELECT A.PRODUCT_ID
, A.PRODUCT_NAME
, B.USER_ID
, B.LAST_USED_AT
FROM PRODUCTS A
LEFT JOIN PRODUCTS_LAST_USAGE B ON A.PRODUCT_ID = B.PRODUCT_ID
ORDER BY B.USER_ID, B.LAST_USED_AT DESC, A.PRODUCT_NAME;

Posts, comments and advanced sql filtering

I have a database table of posts. I have another db table with comments, with a column for comment_id (ai) and a column for the post_id it's attached to.
I want to make a query list all of my posts once, sorted by last posted comment.
The problem is I can't figure out the sql query to do it. Whatever I try I get all the comments listed too. I'm very skilled or experienced with mysql, but I tried using different "joins". Is there any way to do it?
SELECT posts.*, comments.last_one
FROM posts
LEFT JOIN
(SELECT MAX(updated) as last_one, post_id
FROM comments
GROUP BY post_id
) as comments
ON comments.post_id = posts.id
ORDER BY comments.last_one DESC
SELECT distinct p.* FROM posts as p, comments as c
WHERE p.post_id = c.post_id ORDER BY c.time DESC
I just assumed your relational schema. You might need to adapt the query.
Alternative: create a view with the last_comment_time as a field.
CREATE VIEW posts AS
(SELECT *, (SELECT time FROM comments WHERE post_id = p.post_id ORDER BY time DESC LIMIT 1) as last_comment_time
FROM posts as p);