web-dev-qa-db-ja.com

ハイブSQLは、最新のレコードを見つけます

表は次のとおりです。

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

このようなデータ:

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

上記のデータのように、IDごとに最新のレコード(すべての列ID、名前、年齢、変更済みを含む)を取得したいのですが、正しい結果は次のとおりです。

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

私はこれが好きです:

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

このsqlは正しい結果を得ることができますが、大量のデータがある場合、実行が遅くなります。

**左外部結合なしでこれを行う方法はありますか? **

25
qiulp

Hive SQLには、ドキュメント化されていない機能がほとんどあります(Jiraのバグレポートの1つで発見しました)。たとえば、次のようなテーブルがある場合:

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

あなたはこれを行うことができます:

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

そして結果を得る:

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

Val(最初のstruct要素)が同類の場合、2列目の比較にフォールバックすると思います。また、おそらくnamed_structを使用して、結果の構造体から個々の列を戻すためのよりきれいな構文があるかどうかもわかりませんか?

48
patricksurry

Hive SQLには比較的最近の機能である 分析関数とover節 があります。これは結合なしで仕事をするはずです

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 

ここで行われているのは、サブクエリが、対応する人のIDの最新の変更タイムスタンプを持つlast_modifiedカラムを追加した新しい行を生成することです。 (group byの場合と同様)ここで重要なのは、サブクエリが元のテーブルの行ごとに1行を取得し、そこからフィルタリングすることです。

より単純なソリューションでも機能する可能性があります。

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

ちなみに、同じコードはImpalaでも機能します。

9
Mateo

前の回答で回答されたものとはわずかに異なるアプローチ。

以下の例では、Hive windowing関数を使用して最新のレコードを見つけます。続きを読む 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;
_

変更された文字列は、unix_timestamp(modified,'yyyy-MM-dd hh:mm:ss')を使用してタイムスタンプに変換し、タイムスタンプに基づいて順序を適用します。

6
Rahul Sharma

これを試してみてください:

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

フィドル ここ

左外部結合ソリューション ここ

どっちが速いか教えてください:)

6
Mosty Mostacho

次のような左外部結合を使用しなくても、必要な結果を得ることができます。

select * from test where(id、modified)in(select id、max(modified)from test group by id)

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

0
aditya

これを試して

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

Uが、変更されたmaxを持つ行が同じid行セットのmax ageを持つことを確認できる場合。

試して

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