I recently made an sql driver for all my sql functions and obtaining a random record was one of them.
I adapted solution #3 from this blog after reading the comments and this is what I came up with:
<?php
class MyDatabaseDriver {
protected $DBC;
protected $SEL_RANDOM_OFFSET;
protected $SEL_RANDOM_IMAGE;
function __construct($uname, $password, $table="my_table") {
$this->DBC = new mysqli('localhost', $uname, $password, $table);
if ($this->DBC->connect_errno) {
printf("Connect failed: %s\n", $this->DBC->connect_error);
exit;
}
$this->initialize();
}
function __destruct() { $this->close(); }
protected function initialize() {
$this->SEL_RANDOM_OFFSET = $this->DBC->prepare("SELECT ROUND(RAND() * COUNT(*), 0) AS `offset` FROM `images` WHERE `albumid` = ?");
$this->SEL_RANDOM_IMAGE = $this->DBC->prepare("SELECT `filename` FROM `images` LIMIT ?, 1");
}
function close() {
if (!$this->DBC) return;
$this->SEL_RANDOM_OFFSET->close();
$this->SEL_RANDOM_IMAGE->close();
$this->DBC->close();
$this->DBC = false;
}
function SelectRandomImage($gid) {
$result = false;
$this->SEL_RANDOM_OFFSET->bind_param("i", $gid);
$this->SEL_RANDOM_OFFSET->execute();
$this->SEL_RANDOM_OFFSET->bind_result($result);
if (!$this->SEL_RANDOM_OFFSET->fetch()) {
printf("Select random offset failed: %s\n", $this->SEL_RANDOM_OFFSET->error);
$result = false;
$this->SEL_RANDOM_OFFSET->reset();
return $result;
}
$this->SEL_RANDOM_OFFSET->reset();
$this->SEL_RANDOM_IMAGE->bind_param("i", $result);
$this->SEL_RANDOM_IMAGE->execute();
$this->SEL_RANDOM_IMAGE->bind_result($result);
if (!$this->SEL_RANDOM_IMAGE->fetch()) {
printf("Select random image failed: %s\n", $this->SEL_RANDOM_IMAGE->error);
$result = false;
$this->SEL_RANDOM_IMAGE->reset();
return $result;
}
$this->SEL_RANDOM_IMAGE->reset();
return $result;
}
} ?>
afaik, this is the solution with the best compatibility among different webhosts at the moment (I had some issues using 'get_result' etc when migrating from local to remote hosting). It also takes into account that prepared statements can be repeated many times with better efficiency (which was one of the complaints about #3 is that it has to be repeated to get more than one result) so the objects are reset and kept alive until the class falls out of scope or close is called directly.
EDIT: In my OFFSET selection I use "ROUND" because my database ID column starts with 1, if your ID column starts with 0 you may want to use "FLOOR"