web-dev-qa-db-fra.com

Requête HQL pour entité avec valeur max

J'ai une entité Hibernate qui ressemble à ceci (accesseurs omis par souci de concision):

@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;

}

Je souhaite créer une requête HQL qui récupère la version la plus récente d'une FeatureList. Le type de requête suivant fonctionne:

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

Le problème est que cela ne remplira pas le champ clé, dont j'ai besoin pour contenir la clé de l'enregistrement avec le numéro de version le plus élevé pour la FeatureList donnée. Si j'ajoute f.key dans la sélection, cela ne fonctionnera pas parce qu'il n'est pas dans le groupe par ou un agrégat et si je le mets dans le groupe, le tout cesse de fonctionner et cela me donne simplement chaque version en tant qu'entité distincte.

Alors, quelqu'un peut-il aider?

30
rjsang

La version simple de cette requête ressemble à ceci (en supposant que (name, version) les paires sont uniques):

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

J'ai fait un scénario ici,


Tableau

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           

Résultat en utilisant votre requête d'origine

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

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

Résultat en utilisant la requête souhaitée

>> 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: J'ai essayé cette fois avec MySQL. Ça marche. Je suis à peu près sûr que MS SQL Server possède également l'opérateur IN (...). Vous avez juste besoin d'utiliser session.createNativeQuery() dans Hibernate.


Modifié pour fonctionner sur réponse de axtavt

Comme nous l'avons découvert, cela peut être aussi simple que,

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

Essayez maintenant la même chose en utilisant Hibernate Criteria API ,

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
Adeel Ansari

Vieille question, mais je pensais offrir mon expérience ici également aux futurs utilisateurs:

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

Cela fonctionne très bien, mais si les conditions suivantes sont réunies:

  • MySql v5.5 avec moteur InnoDB
  • Vous savez exactement combien de lignes de résultat vous voulez (OC implique qu'il/elle veut exactement 1 ligne, mais la requête ci-dessus fonctionnerait pour un nombre variable de résultats)

ce qui suit semble être beaucoup plus rapide:

FROM FeatureList ORDER BY version DESC LIMIT 1;

Mes essais sur une requête similaire avec 700 000 enregistrements dans le tableau prennent environ 0,19 s pour l'ancienne version et 0,05 s pour la dernière.

8
Mark McKenna

Que dis-tu de ça,

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

Remarque: essayez avec la fonction createNativeQuery(), c'est une requête Oracle valide, vous n'êtes sûr d'aucune autre base de données.


Une question, la paire, name et version, est-elle unique? Si oui, alors c'est bien, sinon si la paire n'est pas unique, que pensez-vous comment une clé sera sélectionnée? Supposons que l'un des enregistrements de la requête d'origine soit,

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

et supposons qu'il y a 3 keys ayant la même paire. Maintenant, répondez, quelle clé doit être affectée à cet enregistrement? J'espère que vous comprenez mon point.

3
Adeel Ansari

Que diriez-vous d'utiliser distinct + order by au lieu de group by?

select f.id, distinct(f.name), f.version from FeatureList f order by f.version desc
0
Matt Brock