Reputation: 19212
I'm building a site, where users can create posts for free. They can also pay for some extra visibility for their post, meaning that the paid posts will be shown first in search results. Both paid and unpaid posts reside in the same table, and whether a post is paid or not is calculated from the column paid_until
, which simply contains a timestamp.
Now, to make pagination easy, I'd like to be able to get a nice result set from MySQL without additional sorting by PHP, preferably with one query. The total limit for the result set is 20 (as in 20 posts per page), and it should first contain all paid posts (up to 20), and then add 20-n unpaid posts. Both subsets should be sorted by the column modified
within themselves. I need to be able to skip the same results later on by setting an OFFSET
.
I've tried different variations of JOIN
, UNION
, and GROUP BY
, but have not come up with a solution that would produce the results that I want.
Is there some way to write this:
(SELECT * FROM tbl_post WHERE paid_until >= :time_stamp ORDER BY modified DESC)
UNION
(SELECT * FROM tbl_post WHERE paid_until < :time_stamp ORDER BY modified DESC)
so that it would actually work, since UNION
messes up the sorting?
Upvotes: 1
Views: 995
Reputation: 562651
You can sort the result of a UNION query:
(SELECT * FROM tbl_post WHERE paid_until >= :time_stamp)
UNION
(SELECT * FROM tbl_post WHERE paid_until < :time_stamp)
ORDER BY paid_until >= :time_stamp DESC, modified DESC
It's important to keep the parentheses around the last subquery, so the ORDER BY is clearly outside of the subquery. Otherwise SQL applies the ORDER BY to the subquery alone, and then un-sorts it again in the process of doing UNION.
There's no need to sort in the subqueries, just do it in the final step.
But this query can be simplified further:
SELECT * FROM tbl_post
ORDER BY paid_until >= :time_stamp DESC, modified DESC
LIMIT :pagesize OFFSET :page_times_pagesize
Upvotes: 1
Reputation: 34784
Something like:
SELECT col1, col2, col3
FROM ( SELECT *,1 AS SortCol FROM tbl_post WHERE paid_until >= :time_stamp
UNION
SELECT *,2 AS SortCol FROM tbl_post WHERE paid_until < :time_stamp
)AS sub
ORDER BY SortCol, modified DESC
Upvotes: 1