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? **
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?
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.
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.
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)
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.
essaye ça
select id,name,age,modified from test
where modified=max(modified)
group by id,name
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