Selecting Next And Previous

Relates to PHP and MySQL

Starting to get things sorted with the new look business site now, so time to catch up on some blogging. I am going to start off by looking at the common scenario of creating next and previous links for a data result set.

A typical application of the next and previous links is in a News Archive. These links offer intrinsic usability benefits presenting the user with less steps to view articles (eg they do not have to jump back to an index to find the link for the next article). However, selecting neighbouring items from a MySQL database is not that simple.

The obvious solution is to select the item with a unique key one less/more than the current record. But this has two drawbacks. Firstly it requires integers as unique keys, which is not always the case. It also assumes that there is a continuous sequence of unique keys with no breaks. This is rarely the case in a content management system, where some items might be deleted.

For this example I am going to assume that the unique key is not integer based, and instead focus on the timestamp at which each entry was added. So rather than adding or subtracting directly from a unique key, all that is required are a couple of comparative selection queries, having firstly established the timestamp for the current record.


SELECT @now := added 
       FROM table 
       WHERE uid = {x};
SELECT @prev := uid 
       FROM table 
       WHERE added < @now 
       ORDER by added DESC 
       LIMIT 0, 1;
SELECT @next := uid 
       FROM table 
       WHERE added > @now 
       ORDER by added ASC 
       LIMIT 0, 1;

This in itself is one solution, if, rather than creating SQL variables, the required data was just extracted directly in the second and third statements. But this will require two separate calls to the database abstraction layer from the API, with two sets of result data to manipulate. The code can be made more manageable at each layer if this is reduced to a single call. Having acquired the unique ID of the next and previous item, it is merely a case of collecting the data.


SELECT IF(uid = @prev,'p','n') as position, uid, title 
       FROM table 
       WHERE (uid = @prev) OR (uid = @next);

In this statement I have created an additional result field position which will be set to p or n for the previous and next records respectively. This is because I generally use PEAR::DB as my database abstraction with a set of custom functions. In this scenario, the first three statements can be executed in the standard way, $dbh->query($query), and the final statement called via $dbh->getAssoc($query). This will return an associative array of a maximum two items with keys of either n or p and the corresponding data.

Of course there is always more than one way of doing things, and I feel the logic of this alternative approach quite elegant, however, with limited permissions on my current servers, I can only test this on my local machine.


SELECT @row := 0;
DROP TABLE IF EXISTS temptable;
CREATE TEMPORARY TABLE temptable 
       SELECT @row := @row + 1 as rownum, table.* FROM table
       ORDER BY added DESC;
SELECT @current := rownum 
       FROM temptable 
       WHERE uid = {x};
SELECT IF(rownum < @current, 'n', 'p') as position, uid, title
       FROM temptable 
       WHERE (rownum = @current - 1) OR (rownum = @current + 1)

The code utilises the CREATE TEMPORARY TABLE statement to create a replica of the data that is being examined, and adding an additional field to the temporary table to act as a unique integer identifier (rownum). This resolves the problem outlined initially regarding the datatype of unique indexes, and sequence gaps.

There are no doubt copious other solutions to this problem. Of course an obvious choice is to do the work on the API layer. For example, the entire dataset could be collected and then PHP's array functions used to manipulate the dataset and extract the required information. However, I have found in the past this can get quite kludgey! Doing the work at the Database layer itself does not (appear to) harm the performance of the database, and helps keep all layers in the application that bit purer.

Posted on Friday, Jan 30, 2004 at 00:45:22.

Comments on Selecting Next And Previous (4)

α comment

This is a nice engineering solution to the problem :) I would guess it is fast also, here is another solution, with does the same job, but not as nice maby :) (presumtion: you already have the publish date from the post you want to fetch previous and next link for stored in $published. iow: already fetch the article)

SELECT published AS prev_published, title AS prev_title FROM post WHERE published < $published ORDER by published DESC LIMIT 0,1;

SELECT published AS next_published, title AS next_title FROM post WHERE published > $published ORDER BY published LIMIT 0,1;

Simple? Requires 2 SELECTs though… :( BTW, strict on comments: > maps to greater-than sign < maps to less-than sign ; maps to semicolon hehe

Posted by Magne
Tuesday, Aug 03, 2004 at 23:01:37

β comment

Thankyou Magne! And what you suggest is a simple method (once the publish date of current article is known).

Generally I like to keep the PHP layer to a necessary minimum, hence the incentive to utilise the MySQL API as outlined above to return a single associative array that can be passed straight to a template.

And, YES, my comments system is *poor*. Will improve it when time allows. Congrats on the ingenuity :)

Posted by Tom
Wednesday, Aug 04, 2004 at 00:57:45

γ comment

Magne - thank you for the tip on the SELECT statements. It's exactly what I've been looking for.

Tom - great stuff on your blog!

Posted by nprajan
Wednesday, Sep 07, 2005 at 21:20:23

δ comment

As of MySQL 4.1, you can now accomplish this through the use of subqueries:


SELECT @now:=added,
       ( SELECT added
         FROM   table
         WHERE  added<@now
         ORDER  by added DESC
         LIMIT  0,1
       ) AS prev,
       ( SELECT added
         FROM   table
         WHERE  added>@now
         ORDER  by added ASC
         LIMIT  0,1
       ) AS next
FROM   table
ORDER  by added;

Posted by Kevin
Tuesday, Jan 17, 2006 at 14:32:51

Breadcrumbs Trail

[ Home ] -> TW Blog -> Jan 04 -> Selecting Next And Previous
Site Map

The Severn Solutions website achieves the following standards:

[ XHTML 1.0 ] [ CSS 2 ] [ WAI AA ] [ Bobby AA ]

Page compiled in 0.030 seconds