4Trabes Historias de una empresa en 100 metros cuadrados

El blog de Trabe Soluciones

Obtener n registros aleatorios (Oracle,MySQL+Hibernate) utilizando HQL

| | Comentarios

Este problema me ha dado la lata un buen rato. Me animo con este post porque no he encontrado prácticamente ninguna información sobre cómo hacer esto. El problema que queremos solucionar es sencillo de explicar:

Tenemos una entidad llamada FooData. Queremos recuperar n objetos fooData aleatorios.

Suponiendo que la tabla correspondiente a FooData es foo, en mysql podemos resolver nuestro problema con una query tal que así:

1
select * from foo order by rand()  limit n;

En Oracle 9 la query es un poco más compleja, sobre todo por la parte de limitar el número de registros a devolver, que se resuelve con una subquery:

1
2
3
select * from (
  select * from rolleruser order by dbms_random.value)
where rownum < n

Una vez que sabemos resolver nuestro problema en SQL y que ya hemos visto que no hay una solución estándar, vamos a tratar de implementarlo mediante HQL.

La parte de obtener únicamente n resultados es trivial, ya que disponemos del método org.hibernate.Query.setMaxResults(int arg0). El esqueleto de nuestro código sería parecido a:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Session session =
	((HibernatePersistenceStrategy)this.strategy).getSession();
Dialect currentDialect =
	((SessionFactoryImplementor) session.getSessionFactory()).getDialect();

String queryString = null;

if (currentDialect instanceof Oracle9Dialect) {
 	// Oracle 9 specific
    	queryString = "CUSTOM_QUERY_ORACLE";
}
else {
	// MYSQL specific
    	queryString = "CUSTOM_QUERY_MYSQL";
}

Query query = session.createQuery(queryString);
query.setMaxResults(amount);

Donde será necesario sustituir CUSTOM_QUERY_ORACLE y CUSTOM_QUERY_MYSQL por los valores adecuados en cada una de las ramas del if.

HQL para obtener n registros aleatorios con MySQL

Para hacer esta consulta con HQL “sobre” mysql, la traducción es casi directa. Eliminamos el limit, pues ya limitamos el número de resultados con setMaxResults() y cambiamos el nombre de la tabla por el nombre de la entidad, de modo que en el ejemplo anterior donde teníamos CUSTOM_QUERY_MYSQL, ahora tendremos:.

1
from FooEntry foo order by rand();

HQL para obtener n registros aleatorios con Oracle 9

Esta traducción es casi tan trivial como la anterior, pero con matices. Utilizando la misma lógica que en el apartado anterior llegamos a una query más o menos así:

1
from FooEntry foo order by dbms_random.value;

Esta query no funciona, pues nuestro amigo Hibernate trata de buscar el atributo value de la entidad dbms_random. Y no encuentra el mapping, porque no lo hay. Solo tenemos que buscar la manera de que la BD reciba la query adecuada. Tras un buen rato de ensayo-error, llegamos a la siguiente solución:

1
from FooEntry foo order by dbms_random.random()

Ahora nuestro driver si que es capaz de “saber” que dbms_random.random() no es cosa suya y que lo único que tiene que hacer es dejar pasar ese trocito de query para que Oracle juegue con el.

Sustituyendo CUSTOM_QUERY_ORACLE por esta última query, ya tendremos el puzzle completo.

Lo sentimos, pero los comentarios están cerrados

Gracias por la instrucción query.setMaxResults(amount); era justo lo que necesitaba.

Saludos

Muchísimas gracias por tu tiempo y por el dbms_random.random(). Nos ha servido de mucho.

29/Oct/2008 Heiner

Gracias, me sirvió de mucho

23/Nov/2009 Johnny

Excelente, me funciona a la perfección, saludos

23/Jun/2011 Chigen

Muchas gracias! dbms_random.random() era justo lo que buscaba :) ! saludos!

12/Aug/2011 Agustin