Consulta HQL para entidad con valor máximo


Tengo una entidad hibernada que se parece a esto (accessors ommitted for brevity):

@Entity
@Table(name="FeatureList_Version")
@SecondaryTable(name="FeatureList",
    pkJoinColumns=@PrimaryKeyJoinColumn(name="FeatureList_Key"))
public class FeatureList implements Serializable {

    @Id
    @Column(name="FeatureList_Version_Key")
    private String key;

    @Column(name="Name",table="FeatureList")
    private String name;

    @Column(name="VERSION")
    private Integer version;

}

Quiero crear una consulta HQL que recupere la versión más actualizada de una FeatureList. El siguiente tipo de consulta funciona:

Select f.name, max(f.version) from FeatureList f group by f.name

El problema es que no rellenar el campo de clave, que necesito para contener la clave del registro con el número de versión más alto para el FeatureList. Si añado f. key en el select no funcionará porque no está en el grupo por o un agregado y si lo pongo en el grupo por todo el asunto deja de funcionar y sólo me da cada versión como una entidad separada.

Entonces, ¿alguien puede ayudar?

Author: rjsang, 2010-12-21

5 answers

La versión directa de esta consulta se ve así (suponiendo que (name, version) los pares son únicos):

select f from FeatureList f 
where f.version = 
     (select max(ff.version) from FeatureList ff where ff.name = f.name)
 55
Author: axtavt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-21 12:52:06

Hice un escenario aquí,


Cuadro

key          name                 version         
----------- -------------------- ----------- 
1           adeel                1           
2           adeel                2           
3           adeel                3           
4           ahmad                1           
5           ahmad                2           
6           ahmad                3           
7           ahmad                4           
8           ansari               1           
9           ansari               2           
10          ansari               3           

Resultado usando su consulta original

>> select f.name, max(f.version) from FeatureList f group by f.name

name                 max(f.version) 
-------------------- ------------ 
adeel                3            
ahmad                4            
ansari               3            

Resultado usando la consulta deseada

>> select fl.* from FeatureList fl 
   where (fl.name, fl.version) in (select f.name, max(f.version) 
                                           from FeatureList f group by f.name);

key          name                 max(fl.version)  
----------- -------------------- ----------- 
3           adeel                3           
7           ahmad                4           
10          ansari               3           

NB: Lo probé usando MySQL esta vez. Está funcionando. Estoy bastante seguro de que MS SQL Server también tiene el operador IN (...). Solo necesita usar session.createNativeQuery() en Hibernación.


Editado para trabajar en la respuesta de axtavt

Como descubrimos esto puede ser hecho tan simple como,

select f from FeatureList f 
where f.version = 
     (select max(ff.version) from FeatureList ff where ff.name = f.name)

Ahora intente lo mismo usando API de Criterios de Hibernación ,

DetachedCriteria versions = DetachedCriteria.forClass(FeatureList.class, "f")
    .setProjection( Property.forName("f.version").max())
    .add(Property.forName("f.name").eqProperty("fl.name"));

session.createCriteria(FeatureList.class, "fl")
    .add( Property.forName("fl.version").eq(versions) )
    .list();
 13
Author: Adeel Ansari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:23

Vieja pregunta, pero pensé en proporcionar mi experiencia aquí también para futuros usuarios:

select f from FeatureList f where f.version = 
 (select max(ff.version) from FeatureList ff where ff.name = f.name);

Esto funciona muy bien, pero si se cumple lo siguiente:

  • MySQL v5. 5 con motor InnoDB
  • Sabe exactamente cuántas filas de resultados desea (OC implica que quiere exactamente 1 fila, pero la consulta anterior funcionaría para un número variable de resultados)

Lo siguiente parece ser significativamente más rápido:

FROM FeatureList ORDER BY version DESC LIMIT 1;

Mis ensayos sobre una consulta similar con 700.000 los registros en la tabla toman alrededor de 0.19 s para la primera versión y 0.05 s para la segunda.

 8
Author: Mark McKenna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-30 13:41:06

¿Qué tal esto,

from FeatureList fl where (fl.name, fl.version) in (
               select f.name, max(f.version) from FeatureList f group by f.name)

Nota: Pruebe con createNativeQuery(), es una consulta Oracle válida, no está seguro acerca de cualquier otra base de datos.


Una pregunta, es el par, name y version, único? Si es así, entonces está bien, de lo contrario, si el par no es único, ¿qué piensas cómo se seleccionará una clave? Supongamos que uno de los registros de la consulta original es,

f.name         max(f.version)
------         --------------
Django         1.2

Y asumir que hay 3 key s que tienen el mismo par. Ahora responda, ¿qué clave debe asignarse a este registro? Me espero que estés entendiendo mi punto.

 2
Author: Adeel Ansari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-21 11:37:05

¿Qué tal usar distinct + order by en lugar de group by?

select f.id, distinct(f.name), f.version from FeatureList f order by f.version desc
 0
Author: Matt Brock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-21 16:55:45