views:

596

answers:

3

Original Question:

I am currently using Zend Framework with Zend_Db_*, and I am selecting three random rows from a table:

$category->getTable()->select()->order(new Zend_Db_Expr('RAND()'))->limit('3')

Where $category is a Zend_Db_Table_Row. I would like to grab three random rows, but have those three rows ordered by the column named name.

Changing the ->order() to the following had no effect:

->order(array(new Zend_Db_Expr('RAND()'), 'name ASC'))

As the entries are still showing up un-ordered, and still random.

Zend Framework solutions appreciated, but I can adapt other solutions to fit within my project.


I am aware of the scaling issues with using RAND(), the database will never get big enough for this to become an issue, the day it does I won't have to worry about maintaining it, the robots will, as I'll be long dead! :-P


Answer

For those wondering how this was ultimately completed using Zend_Db_Select, this is what it came down to use a sub-select within the Zend_Db_Select (I was using $category->findDefault_Model_projects() to find the dependent rowset, but that does not allow me to use the select() as a subselect until ZF-6461 fixes the issue, I am stuck with what I have):

$projects = new Default_Model_Projects();
$subselect = $projects->select()->order(new Zend_Db_Expr('RAND()'))->limit('3')->where('cid = ?', $category->id, Zend_Db::INT_TYPE);
$db = $projects->getAdapter();
$select = $db->select()->from(array("c" => new Zend_Db_Expr("({$subselect})")))->order('name');

$stmt = $select->query();
$projects = $stmt->fetchAll();

The generated SQL is:

SELECT `c`.* FROM (SELECT `projects`.* FROM `projects` WHERE (cid = 1) ORDER BY RAND() LIMIT 3) AS `c` ORDER BY `name` ASC

From there $projects contains the standard row set which can be iterated over much like any other database queries, the only thing it does not do is stick it in a table specific row/rowset class, which could have its downsides.

+3  A: 

Your initial solution is not correct because this query will generate a random value for each row and the order the rows based on it, sorting by name only if random values are equal (which is highly improbable).

The problem can be solved with a subquery like the one below

select * from (select * from categories order by rand() limit 3) c order by name

I'm leaving to you the task of translating this into Zend_Db language.

Adam Byrtek
When I get back to work on Monday I will test this solution. I have up-voted for letting me know why mine would not work!
X-Istence
Thanks, hope it helps!
Adam Byrtek
This worked perfectly, using the Zend Framework Zend_Db_Select class the query ended up being: SELECT `c`.* FROM (SELECT `projects`.* FROM `projects` WHERE (cid = 1) ORDER BY RAND() LIMIT 3) AS `c` ORDER BY `name` ASC
X-Istence
A: 

Why not just create a Rowset Subclass function that sorts the data by name?

gnarf
A: 

Try this:

$select = $this->select();
$select->order('RAND(), name');
$select->limit(3);
return $this->fetchAll($select);

That works for me here so it should work for you, too.

Richard Knop
That is exactly what I did if you look at my posting, except I used Zend_DB_Expr and an array to pass it to order() which is how the documentation says it should be used if you are using a database expression.The SQL generated is exactly the same:SELECT `projects`.* FROM `projects` WHERE (`cid` = 1) ORDER BY RAND(), `name` ASC LIMIT 3
X-Istence
The issue is that it does not work, it does not ORDER BY RAND() and then limit to 3, and then sort by name.
X-Istence