Hive sql найти последнюю запись - программирование
Подтвердить что ты не робот

Hive 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 может получить правильный результат, но при массовых данных он работает медленно.

** Есть ли способ сделать это без левого внешнего соединения? **

4b9b3361

Ответ 1

Там почти недокументированная функция Hive SQL (я нашел ее в одном из отчетов об ошибках Jira), который позволяет вам делать что-то вроде argmax() с помощью struct() s. Например, если у вас есть таблица вроде:

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 как-то?

Ответ 2

Существует относительно недавняя функция 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, который имеет последнюю измененную метку времени для соответствующего идентификатора человека. (Подобно тому, что будет делать группа). Ключевым моментом здесь является то, что подзапрос возвращает вам одну строку за строку в вашей исходной таблице, а затем вы отфильтровываете ее.

Есть вероятность, что даже более простое решение работает:

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

Кстати, тот же код будет работать и в Impala.

Ответ 3

Попробуйте это:

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

Скрипка здесь

Левое решение для внешнего соединения здесь.

Дайте нам знать, какой из них работает быстрее :)

Ответ 4

Просто немного другой подход, чем тот, на который был дан ответ в предыдущем ответе.

Ниже приведен пример использования оконной функции улья, чтобы узнать последнюю запись, подробнее здесь

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') затем применяет порядок по метке времени.

Ответ 5

попробуйте это

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

Ответ 6

Если вы можете убедиться, что строка с max-модифицированным также имеет максимальный возраст в том же наборе строк id.

Try

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

Этот запрос выполняет дополнительную группу и менее эффективен, но дает правильный результат -

    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

Вы можете получить требуемый результат без использования внешнего внешнего соединения следующим образом:

выберите * из теста где (id, modified) in (выберите id, max (изменено) из тестовой группы по id)

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