30

表格是:

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可以得到正确的结果,但是当大量数据时,它运行缓慢。

**没有左外连接有没有办法做到这一点?**

4

8 回答 8

53

Hive SQL 有一个几乎没有记录的特性(我在他们的一个 Jira 错误报告中找到了它),它可以让你使用 struct()s 执行类似 argmax() 的操作。例如,如果您有如下表格:

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 (第一个结构元素)存在关系,它将回退到第二列的比较。我还没有弄清楚是否有一种更简洁的语法可以将各个列从结果结构中取出,也许以某种方式使用 named_struct ?

于 2013-11-30T12:57:51.643 回答
11

与先前答案中回答的方法略有不同。

下面的示例使用hive 窗口功能来查找最新记录,请在此处阅读更多信息

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')时间戳。

于 2017-04-20T20:01:41.937 回答
10

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 

这里发生的事情是子查询生成一个新行,其中包含一个额外的列 last_modified,该列具有相应人员 ID 的最新修改时间戳。(类似于 group by 会做的事情)这里的关键是子查询再次让您在原始表中每行一行,然后从中过滤。

即使是更简单的解决方案也有可能起作用:

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

顺便说一句,同样的代码也可以在 Impala 中运行。

于 2015-05-09T15:24:25.093 回答
6

试试这个:

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

在这里拉小提琴。

左外连接解决方​​案在这里

让我们知道哪个跑得更快:)

于 2012-11-23T04:29:01.463 回答
0

如果您可以确保最大修改的行在同一 id 行集中也具有最大年龄。

尝试

select id, name, max(age), max(modified) 
from test
group by id, name
于 2012-11-28T15:52:51.773 回答
0

尝试这个

select id,name,age,modified from test
 where modified=max(modified)
 group by id,name
于 2012-11-23T04:50:01.463 回答
0

假设数据是这样的:

    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

那么上面查询的结果会给你 - (注意重复的 2, b 具有相同的日期时间)

    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

此查询运行一个额外的 group by 并且效率较低但给出了正确的结果 -

    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;

那么上面查询的结果会给你

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

现在,如果我们稍微改进一下查询 - 然后代替 3 个 MR,它只运行一个 Keping 结果相同 -

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

注意 - 如果您的按字段分组产生大量结果,这将减慢速度。

于 2013-03-14T21:41:19.510 回答
0

您可以在不使用左外连接的情况下获得所需的结果,如下所示:

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

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

于 2015-07-23T00:10:02.790 回答