web-dev-qa-db-fra.com

ruche sql trouver le dernier enregistrement

la table est:

create table test (
id string,
name string,
age string,
modified string)

des données comme celle-ci:

id    name   age  modifed
1     a      10   2011-11-11 11:11:11
1     a      11   2012-11-11 12:00:00
2     b      20   2012-12-10 10:11:12
2     b      20   2012-12-10 10:11:12
2     b      20   2012-12-12 10:11:12
2     b      20   2012-12-15 10:11:12

Je souhaite obtenir le dernier enregistrement (y compris chaque identifiant de colonne, nom, âge, modifié), groupe par identifiant.

1     a      11   2012-11-11 12:00:00
2     b      20   2012-12-15 10:11:12

J'aime ça:

insert overwrite table t 
select b.id, b.name, b.age, b.modified 
from (
        select id,max(modified) as modified 
        from test 
        group by id
) a 
left outer join test b on (a.id=b.id  and a.modified=b.modified);

Ce SQL peut obtenir le bon résultat, mais lorsque les données en masse, il fonctionne lentement.

** Y a-t-il un moyen de faire cela sans jointure externe gauche? **

23
qiulp

Il existe une fonctionnalité presque non documentée de Hive SQL (je l'ai trouvée dans l'un de leurs rapports de bogue Jira) qui vous permet de faire quelque chose comme argmax () en utilisant struct () s. Par exemple, si vous avez une table comme celle-ci:

test_argmax
id,val,key
1,1,A
1,2,B
1,3,C
1,2,D
2,1,E
2,1,U
2,2,V
2,3,W
2,2,X
2,1,Y

Tu peux le faire:

select 
  max(struct(val, key, id)).col1 as max_val,
  max(struct(val, key, id)).col2 as max_key,
  max(struct(val, key, id)).col3 as max_id
from test_argmax
group by id

et obtenir le résultat:

max_val,max_key,max_id
3,C,1
3,W,2

Je pense qu'en cas d'égalité sur val (le premier élément struct), la comparaison se fera sur la deuxième colonne. Je n'ai pas non plus déterminé s'il existait une syntaxe plus simple pour extraire les colonnes individuelles de la structure résultante, en utilisant peut-être d'une manière nommée named_struct?

45
patricksurry

Il existe une fonctionnalité relativement récente de Hive SQL, fonctions analytiques et de la clause over . Cela devrait faire le travail sans jointures 

select id, name, age, last_modified 
from ( select id, name, age, modified, 
              max( modified) over (partition by id) as last_modified 
       from test ) as sub
where   modified = last_modified 

Ce qui se passe ici, c'est que la sous-requête génère une nouvelle ligne avec une colonne supplémentaire last_modified qui contient le dernier horodatage modifié pour l'identifiant de la personne correspondante. (Semblable à ce que ferait groupe par) La clé ici est que la sous-requête vous renvoie à nouveau une ligne par ligne dans votre table d'origine et que vous filtrez à partir de cela. 

Il est possible que même la solution la plus simple fonctionne: 

select  id, name, age,  
        max( modified) over (partition by id) last_modified 
from test 
where   modified = last_modified 

À propos, le même code fonctionnerait également dans Impala.

7
Mateo

Essayez ceci:

select t1.* from test t1
join (
  select id, max(modifed) maxModified from test
  group by id
) s
on t1.id = s.id and t1.modifed = s.maxModified

Violon ici .

Solution de jointure externe gauche ici .

Dites-nous lequel est le plus rapide :)

6
Mosty Mostacho

Juste une approche légèrement différente de ce qui a été répondu dans la réponse précédente.

L'exemple ci-dessous utilise la fonction Howing windowing pour trouver le dernier enregistrement, en lire plus ici

SELECT t.id
    ,t.name
    ,t.age
    ,t.modified
FROM (
    SELECT id
        ,name
        ,age
        ,modified
        ,ROW_NUMBER() OVER (
            PARTITION BY id ORDER BY unix_timestamp(modified,'yyyy-MM-dd hh:mm:ss') DESC
            ) AS ROW_NUMBER   
    FROM test
    ) t
WHERE t.ROW_NUMBER <= 1;

La chaîne modifiée est donc convertie en horodatage à l'aide de unix_timestamp(modified,'yyyy-MM-dd hh:mm:ss'), puis en appliquant order by on timestamp.

4
Rahul Sharma

Vous pouvez obtenir le résultat requis sans utiliser de jointure externe gauche comme ceci:

select * from test où (id, modifié) dans (select id, max (modifié) dans le groupe de tests par id) 

http://sqlfiddle.com/#!2/bfbd5/42

0
aditya

Présumer que les données sont comme ceci:

    id      name    age     modifed
    1       a       10      2011-11-11 11:11:11
    1       a       11      2012-11-11 12:00:00
    2       b       23      2012-12-10 10:11:12
    2       b       21      2012-12-10 10:11:12
    2       b       22      2012-12-15 10:11:12
    2       b       20      2012-12-15 10:11:12

alors le résultat de la requête ci-dessus vous donnera - (notez le répété 2, b ayant la même date/heure)

    1       a       11      2012-11-11 12:00:00
    2       b       22      2012-12-15 10:11:12
    2       b       20      2012-12-15 10:11:12

Cette requête exécute un groupe supplémentaire par et est moins efficace mais donne le résultat correct -

    select collect_set(b.id)[0], collect_set(b.name)[0], collect_set(b.age)[0], b.modified
    from
        (select id, max(modified) as modified from test group by id) a
      left outer join
        test b
      on
        (a.id=b.id and a.modified=b.modified)
    group by
      b.modified;

alors le résultat de la requête ci-dessus vous donnera

    1       a       11      2012-11-11 12:00:00
    2       b       20      2012-12-15 10:11:12

Maintenant, si nous améliorons un peu la requête - alors, à la place de 3 MRs, il n’exécute qu’un seul résultat -

    select id, collect_set(name)[0], collect_set(age)[0], max(modified)
    from test 
    group by id;

Remarque - cela ralentira si votre groupe par champ produit des résultats importants.

0
user 923227

essaye ça

select id,name,age,modified from test
 where modified=max(modified)
 group by id,name
0
SRIRAM

Si vous pouvez vous assurer que la ligne qui a max modifié a également un âge maximum dans le même ensemble de lignes. 

Essayer

select id, name, max(age), max(modified) 
from test
group by id, name
0
pensz