Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If db query A does not return enough results, run query B: how to optimize?

I am looking for a good design pattern, or best practice to achieve a situation of "either this query, or else that other query", with best performance and least overhead.

Business logic/program of demans says "all items since Foo", unless that returns less then three items, then "all items". I am refactoring the current code, and cannot come up with a good way to achieve this logic.

Current pseudo-code (Drupal/PHP):

<?php
$result = db_query(
'SELECT n.uid FROM og_ancestry oga ' .
'INNER JOIN node n on n.nid = oga.nid ' .
'WHERE oga.group_nid = %d AND n.created > %d GROUP BY n.uid ' .
'ORDER BY cnt DESC LIMIT %d', $group_nid, $since, $limit);


while ($row = db_fetch_array($result)) {
  $uids[] = $row['uid'];
}

if (count($uids) < 3) {
  $result = db_query(
    'SELECT n.uid FROM og_ancestry oga ' .
    'INNER JOIN node n on n.nid = oga.nid ' .
    'WHERE oga.group_nid = %d GROUP BY n.uid ' .
    'ORDER BY cnt DESC LIMIT %d', $group_nid, $limit);

  while ($row = db_fetch_array($result)) {
    $uids[] = $row['uid'];
  }
}
//...do something with the result.
?>

This code feels "not right", first of all because of DRY: it contains the same query, with one minor difference. I can change that by a bit smarter query-building.

But worse is the fact that I need to hammer the database (query is quite heavy) only to find out that in more then half of the cases, I need to throw away the result and query the db again.

How would you approach such a case?

like image 310
berkes Avatar asked Aug 20 '10 14:08

berkes


2 Answers

If, as you say, "in more then half of the cases, I need to throw away the result and query the db again," then your best bet may be to run only the second query and then evaluate the resultant dataset locally, discarding records if appropriate. It's really more a matter of moving the complexity around than it is of reducing complexity, but at least there's just one trip to the database.

If you ORDER BY n.created DESC, the filtering could simply look at the third record, and if it's earlier than foo, you're done; otherwise, you need to find the first record before foo and discard it and subsequent records.

like image 140
Carl Manaster Avatar answered Sep 27 '22 17:09

Carl Manaster


You could use a single CASE/WHEN query to see if the first query returns enough. If so, use the THEN block. If not use the ELSE block. That would save you the second roundtrip to the database.

like image 45
Gordon Avatar answered Sep 27 '22 18:09

Gordon