2012-11-23 8 views
18

der Tabelle zu finden ist:Hive SQL, um die letzte Aufzeichnung

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

Daten wie folgt aus:

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 

ich die neueste Aufzeichnung erhalten möchten (einschließlich jeder colums ID, Name, Alter, modifed) Gruppe von id, wie die oben genannten Daten ist das richtige Ergebnis:

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

ich tun, wie folgt:

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

Diese sql kann das richtige Ergebnis erhalten, aber wenn Massendaten, läuft es langsam.

** Gibt es eine Möglichkeit, dies ohne linke äußere Verbindung zu tun? **

+0

danke für die Frage und die Antwort, lösten sie mein Problem vollständig! – eleforest

Antwort

4

geben diesem einen Versuch:

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 

Fiddle here.

Linke äußere Verbindung Lösung here.

Lassen Sie uns wissen, welche schneller läuft :)

+0

Ich führe Ihre SQL: – qiulp

+0

Ihre SQL, Zeitaufwand: 325.579 Sekunden Gesamt MapReduce CPU Zeit: 11 Minuten 36 Sekunden 130 ms, 6 Jobs. mein sql, Zeitaufwand: 220.736 Sekunden Gesamt MapReduce CPU Zeitaufwand: 12 Minuten 13 Sekunden 80 ms, 5 Job. – qiulp

+0

Es sieht so aus, als würde Ihr SQL die Leistung nicht verbessern. – qiulp

0

versuchen, diese

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

Alter kann geändert werden, so kann es nicht "Gruppe von ID, Name, Alter", einfach so: 1 a 10 2011-11-11 11:11:11 1 a 11 2012-11-11 12:00 : 00 – qiulp

0

Wenn u sicher, dass die Zeile machen, die max hat auch in derselben ID Reihe mit max Alter hat geändert.

Versuchen

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

Presume der Daten ist wie folgt:

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 

dann das Ergebnis der obigen Abfrage werden Sie geben - (man beachte die wiederholte 2, b das gleiche Datum Zeit haben)

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 

Diese Abfrage wird eine zusätzliche Gruppe von und ist weniger effizient, aber gibt das richtige Ergebnis -

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; 

dann das Ergebnis der obigen Abfrage Sie

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

Jetzt geben, wenn wir die Abfrage ein wenig verbessern - dann anstelle von 3 MRs läuft es nur ein Keping das Ergebnis gleich -

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

Hinweis - dies wird verlangsamen, wenn Ihre Gruppe nach Feld große Ergebnisse produziert.

32

Es gibt eine fast undokumentierte Funktion von Hive SQL (ich fand es in einem ihrer Jira Bug-Berichte), die Sie etwas wie argmax() mit struct() s tun können.Wenn Sie zum Beispiel haben eine Tabelle wie:

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 

Sie dies tun können:

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 

und das Ergebnis erhalten:

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

ich bei Bindern an val denken (die erstes Strukturelement) wird auf den Vergleich in der zweiten Spalte zurückgegriffen. Ich habe auch nicht herausgefunden, ob es eine bessere Syntax gibt, um die einzelnen Spalten wieder aus der resultierenden Struktur herauszuholen, vielleicht irgendwie mit named_struct?

+1

Das ist eine großartige Lösung, ich mag es sehr! Vielen Dank. –

+0

wunderbar, das sollte viel mehr upvoted! –

6

Es gibt eine relativ neue Funktion von Hive SQL, analytic functions and the over clause. Dies sollte die Arbeit erledigen, ohne

verbindet
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 

Was hier vor sich geht, ist, dass die Unterabfrage eine neue Zeile mit einer zusätzlichen Spalte last_modified erzeugt, welche die neueste modifizierte Zeitstempel für die entsprechende Person id hat. (Ähnlich wie bei der Gruppierung nach) Der Schlüssel hier ist, dass die Unterabfrage Ihnen in Ihrer Originaltabelle wieder eine Zeile pro Zeile liefert und Sie danach filtern.

Es gibt eine Chance, dass auch die einfachere Lösung funktioniert:

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

By the way, der gleiche Code würde in Impala arbeiten, auch.

0

Sie können das gewünschte Ergebnis erhalten, ohne linke äußere wie diese kommen mit:

select * from Test, bei dem (id, modifiziert) in (select id, max (modifiziert) aus Testgruppe von id)

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

3

Nur etwas anders Ansatz als was in der vorherigen Antwort beantwortet wurde.

Im Folgende Beispiel wird hive Windowing Funktion der aktuelle Datensatz, um herauszufinden, lesen Sie mehr here

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; 

Die modifizierte String so ist die Umwandlung es mit unix_timestamp(modified,'yyyy-MM-dd hh:mm:ss') dann zu, um der Anwendung von auf Zeitstempel.

Verwandte Themen