2012-11-23 29 views
18

bảng là:hive sql tìm ra kỷ lục mới nhất

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

dữ liệu như thế này:

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 

Tôi muốn để có được những kỷ lục mới nhất (bao gồm tất cả các colums id, tên, tuổi, modifed) nhóm bởi id, như các dữ liệu trên, kết quả đúng là:

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

tôi làm như thế này:

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 này có thể nhận được kết quả phù hợp, nhưng khi dữ liệu khối lượng , nó chạy chậm.

** Có cách nào để thực hiện việc này mà không cần tham gia bên ngoài bên trái không? **

+0

cảm ơn câu hỏi và câu trả lời, họ đã giải quyết hoàn toàn vấn đề của tôi! – eleforest

Trả lời

4

này cung cấp cho một thử:

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 

Fiddle here.

Giải pháp nối ngoài bên trái here.

Cho chúng tôi biết cái nào chạy nhanh hơn :)

+0

Tôi chạy sql của bạn: – qiulp

+0

sql của bạn, Thời gian thực hiện: 325.579 giây Tổng thời gian sử dụng CPU MapReduce: 11 phút 36 giây 130 msec, 6 công việc. sql của tôi, Thời gian thực hiện: 220,736 giây Tổng thời gian MapReduce Thời gian chạy CPU: 12 phút 13 giây 80 msec, 5 công việc. – qiulp

+0

Dường như sql của bạn không cải thiện hiệu suất. – qiulp

0

thử này

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

tuổi có thể thay đổi, vì vậy không thể "nhóm theo id, tên, tuổi", giống như sau: 1 a 10 2011-11-11 11:11:11 1 a 11 2012-11-11 12:00 : 00 – qiulp

0

Nếu u có thể đảm bảo rằng hàng trong đó có max cũng sửa đổi có độ tuổi tối đa trong cùng một tập hợp hàng id.

Hãy thử

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

Hãy cho dữ liệu là như thế này:

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 

sau đó là kết quả của các truy vấn trên sẽ cung cấp cho bạn - (chú ý lặp đi lặp lại 2, b có thời gian cùng ngày)

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 

Truy vấn này chạy một nhóm bổ sung theo và kém hiệu quả hơn nhưng đưa ra kết quả chính xác -

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; 

sau đó là kết quả của các truy vấn trên sẽ cung cấp cho bạn

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

Bây giờ nếu chúng ta cải thiện truy vấn một chút - sau đó ở vị trí của 3 bà nó chạy chỉ có một Keping kết quả tương tự -

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

Lưu ý - điều này sẽ chậm lại trong trường hợp nhóm của bạn theo trường tạo ra kết quả lớn.

32

Có một tính năng gần như không có giấy tờ của Hive SQL (tôi đã tìm thấy nó trong một trong các báo cáo lỗi Jira của họ) cho phép bạn làm điều gì đó giống như argmax() sử dụng struct() s.Ví dụ nếu bạn có một bảng như:

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 

Bạn có thể làm điều nà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 

và nhận được kết quả:

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

Tôi nghĩ rằng trong trường hợp của các mối quan hệ trên val (các yếu tố cấu trúc đầu tiên) nó sẽ giảm trở lại để so sánh trên cột thứ hai. Tôi cũng đã không tìm ra cho dù có một cú pháp neater để nhận được các cột riêng lẻ trở lại trong cấu trúc kết quả, có thể sử dụng named_struct bằng cách nào đó?

+1

Đây là một giải pháp tuyệt vời, tôi rất thích nó! Cảm ơn nhiều. –

+0

tuyệt vời, điều này nên được upvoted nhiều hơn nữa! –

6

Có một tính năng tương đối gần đây của Hive SQL, analytic functions and the over clause. Điều này sẽ thực hiện công việc mà không tham gia

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 

gì đang xảy ra ở đây là subquery tạo ra một dòng mới với một cột phụ LAST_MODIFIED trong đó có mới nhất timestamp sửa đổi cho id người tương ứng của. (Tương tự như nhóm sẽ làm gì) Chìa khóa ở đây là truy vấn phụ sẽ đưa bạn trở lại một hàng trên mỗi hàng trong bảng ban đầu của bạn và sau đó bạn lọc từ đó.

Có một cơ hội mà ngay cả những giải pháp đơn giản hoạt động:

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

Bằng cách này, cùng mã sẽ làm việc trong Impala, quá.

0

Bạn có thể nhận được kết quả cần thiết mà không cần sử dụng bên ngoài trái tham gia như thế này:

select * from kiểm tra nơi (id, sửa đổi) trong (chọn id, max (sửa đổi) từ nhóm thử nghiệm bằng id)

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

3

Cách tiếp cận hơi khác so với những gì đã được trả lời trong câu trả lời trước đó.

Dưới đây ví dụ sử dụng hive windowing chức năng để tìm ra kỷ lục mới nhất, đọc thêm 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; 

Các sửa đổi là chuỗi để chuyển đổi nó để đánh dấu thời gian sử dụng unix_timestamp(modified,'yyyy-MM-dd hh:mm:ss') sau đó áp dụng trật tự bằng cách vào dấu thời gian.