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 может получить правильный результат, но при массовых данных он работает медленно.
** Есть ли способ сделать это без левого внешнего соединения? **
Ответы
Ответ 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