Counter queries for complex, none-distinct SQL in Drupals Pager system.

I think everyone knows these moments: You have a problem, a question. And just by asking that question, the answer pops up in your head. It happens to me often, when programming. It is obvious: by asking the question, you have to analyze and simplify the problem. And by doing so

Today, there was another great way Stackoverflow helped me: I had a problem with a Drupal pager-query on a none-distinct SQL query. And right when I was finishing up, the answer struck me. But because I spent so much effort in the question, and I don’t want to forget, I decided to share it.


Drupal uses pager_query if you wish to get a limited result, for used as a paged, list.

A simple example would be (I am aware of my code not adhering to Drupal standards, done that for simplicity):

$nodes = pager_query('SELECT title, created FROM node WHERE published = 1', 20, 0);
while ($node = db_fetch_object($nodes)) {
   $html .= "$node->title ($node->created)";
}
$html .= theme('pager'); //This collects "magic" variables set by pager_query to build a string containing pagerlinks.

Now, I need to tackle a much more complex query, one that is not distinct as above. I am not sure if I should solve this in the domain of SQL, or rather in the domain of Drupal/PHP.

session<->node is an N:1 relation: any node has_many sessions. A session has_one node.

$nodes = pager_query('SELECT node.nid, node.title, node.created, sessions.time, sessions.sid FROM node INNER JOIN sessions ON session.nodes_nid = node.nid WHERE published = 1', 20, 0);
while ($node = db_fetch_object($nodes)) {
   $n->title = $node->title;
   $n->nid = $node->nid;
   unset($node->title, $node->nid);
   $n->sessions[$node->sid] = $node;
   $items[$n->nid] = $n;
}

Above routine allows me to query the database ONCE, fetch nodes that have_many playdates, and collect them in a list that:

  • has one row per $node.
  • each $node row has a list of all its associated sessions under $node->sessions.

However, pager_query lists one item for each row, instead of using a smarter counter query.This is where the answer became clear

And so, the answer is really simple: counter query

the last parameter of pager_query() is an alternative query to be used as counter. In the abovementioned example. that would be:

$sql = 'SELECT node.nid, node.title, node.created, sessions.time, sessions.sid FROM node INNER JOIN sessions ON session.nodes_nid = node.nid WHERE published = 1';
$counter = 'SELECT COUNT(DISTINCT(node.nid) FROM node INNER JOIN sessions ON session.nodes_nid = node.nid WHERE published = 1';
pager_query($sql, 20, 0, $counter);

About the author: Bèr Kessels is an experienced webdeveloper with a great passion for technology and Open Source. A golden combination to implement that technology in a good and efficient way. Follow @berkes on Mastodon. Or read more about Bèr.