When I was deciding how many recent posts to display on the main age of Soylent Red I had two concerns: that each post should have its place for a certain minimum time period, and that the page should not empty of content for lack of recent enough posts. The minimum time period right now is one week. The minimum number of posts at any time is five. That means that if I don't post for a week, the last five posts will still be here. If I post ten times in one day, there'll be a whole load of posts on here until they drop off the front page a week hence (assuming they're replaced with new content).
The SQL query for this is a straightforward union: (SELECT * FROM entries WHERE posttime>'cutoff' AND status='published' ORDER BY posttime DESC) UNION (SELECT * FROM entries WHERE status='published' ORDER BY posttime DESC LIMIT 5); Here I've simplified the long list of fields to return to a simple "*"—I don't recommend using the universal selector in practice. Also, "cutoff" represents a datetime value for one week ago computed in the PHP source. This is (essentially) the code I had in place until just now, but there's a bug in it.
This is a UNION of two SELECTs, each of which is ordered by descending timestamps, so the first item returned is the most recent post, as you would want it when displaying posts on a blog. But when the UNION operator is applied to the two sets of results, the order is no longer guaranteed. The posts can come out in any order at all. Up until now they had always been returned in the order I wanted, or at least I hadn't notced otherwise, but there had been no guarantee.
The solution to this ordering problem, once it has been identifieed, is easy. Just add an extra ORDER BY posttime DESC clause at the end of the statement and you're done. In fact, the first ORDER BY clause is made redundant by this, so it can be removed. The ORDER BY clause in the second SELECT interacts with the LIMIT clause, so it needs to remain.
After this jiggery-pokery the final, correct query is: (SELECT * FROM entries WHERE posttime>'cutoff' AND status='published') UNION (SELECT * FROM entries WHERE status='published' ORDER BY posttime DESC LIMIT 5) ORDER BY posttime DESC;
Next time: How to get the full content of every post and its associated comments in a single SELECT. (Not really. That's just something I have on my list of things to do now that I know how. Soylent Red does it in two queries.)
Comments:
Fri, 30th Mar 2007 (07:48)
Fri, 30th Mar 2007 (14:47)