【问题标题】:hive sql find the latest recordhive sql查找最新记录
【发布时间】:2012-11-11 11:05:58
【问题描述】:

表格是:

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

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

【问题讨论】:

  • 感谢您的提问和回答,他们彻底解决了我的问题!

标签: sql group-by hive max


【解决方案1】:

Hive SQL 有一个几乎没有记录的特性(我在他们的一个 Jira 错误报告中找到了它),它可以让您使用 struct() 执行类似 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?

【讨论】:

  • 这是一个很棒的解决方案,我非常喜欢它!非常感谢。
  • 能否解释一下col1、col2、col3属性的作用?
  • col1, ... 只是(未命名的)结构的默认字段名称。写更详细的max(named_struct('val', val, 'key', key, 'id', id)).id as max_id 可能会更清楚
【解决方案2】:

与上一个答案中回答的方法略有不同。

以下示例使用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') 将其转换为时间戳,然后在时间戳上应用 order by。

【讨论】:

  • 我想知道,我们需要 unix_timestamp 修饰符吗?如果 modified 是字符串,那就没必要了吧?
【解决方案3】:

Hive SQL 有一个相对较新的功能,analytic functions and the over clause。这应该可以在没有连接的情况下完成工作

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 中运行。

【讨论】:

  • 如何处理相同的 max(modified)?
【解决方案4】:

试试这个:

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

小提琴here.

左外连接解决方​​案here

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

【讨论】:

  • 你的 sql,耗时:325.579 秒 MapReduce CPU 总耗时:11 分 36 秒 130 毫秒,6 个作业。我的 sql,耗时:220.736 秒 MapReduce CPU 总耗时:12 分 13 秒 80 毫秒,5 个作业。
  • 请注意,您的查询与我发布的第一个查询相同(我刚刚意识到),但您的查询使用了不必要的左连接。内部连接就足够了。那么我提供的真正的左外连接解决方​​案呢?这可能需要更多时间。顺便说一句,当然,请确保仅将 t1.* 替换为必要的字段。
  • "Hive 中仅支持等式连接、外连接和左半连接。Hive 不支持非等式条件的连接条件,因为很难将这些条件表示为 map/reduce作业。另外,在 Hive 中可以加入两个以上的表。 left join .. on .. 和 t1.modifed
【解决方案5】:

试试这个

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

【讨论】:

  • 年龄可以更改,所以不能“按id、姓名、年龄分组”,就像这样:1 a 10 2011-11-11 11:11:11 1 a 11 2012- 11-11 12:00:00
【解决方案6】:

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

试试

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

【讨论】:

    【解决方案7】:

    假设数据是这样的:

        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;
    

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

    【讨论】:

      【解决方案8】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2021-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多