2012-06-25 38 views
6

Tôi có bảng đại diện cho việc sử dụng sản phẩm, giống như nhật ký. Việc sử dụng sản phẩm được ghi lại dưới dạng nhiều dấu thời gian, tôi muốn đại diện cho cùng một dữ liệu bằng cách sử dụng các phạm vi thời gian.Thu gọn nhiều hàng có dấu thời gian liền kề

Nó trông giống như thế này (PostgreSQL 9.1):

userid | timestamp   | product 
------------------------------------- 
001 | 2012-04-23 9:12:05 | foo 
001 | 2012-04-23 9:12:07 | foo 
001 | 2012-04-23 9:12:09 | foo 
001 | 2012-04-23 9:12:11 | barbaz 
001 | 2012-04-23 9:12:13 | barbaz 
001 | 2012-04-23 9:15:00 | barbaz 
001 | 2012-04-23 9:15:01 | barbaz 
002 | 2012-04-24 3:41:01 | foo 
002 | 2012-04-24 3:41:03 | foo 

Tôi muốn sụp đổ hàng có chênh lệch thời gian với thời gian trước đó là ít hơn một đồng bằng (nói: 2 giây), và nhận được các bắt đầu thời gian và thời gian kết thúc, như thế này:

userid | begin    | end    | product 
---------------------------------------------------------- 
001 | 2012-04-23 9:12:05 | 2012-04-23 9:12:09 | foo 
001 | 2012-04-23 9:12:11 | 2012-04-23 9:12:13 | barbaz 
001 | 2012-04-23 9:15:00 | 2012-04-23 9:15:01 | barbaz 
002 | 2012-04-24 3:41:01 | 2012-04-24 3:41:03 | foo 

Xin lưu ý rằng việc sử dụng liên tiếp của cùng một sản phẩm được chia thành hai hàng nếu việc sử dụng của họ là hơn del ta (2 giây, trong ví dụ này) cách nhau.

create table t (userid int, timestamp timestamp, product text); 

insert into t (userid, timestamp, product) values 
(001, '2012-04-23 9:12:05', 'foo'), 
(001, '2012-04-23 9:12:07', 'foo'), 
(001, '2012-04-23 9:12:09', 'foo'), 
(001, '2012-04-23 9:12:11', 'barbaz'), 
(001, '2012-04-23 9:12:13', 'barbaz'), 
(001, '2012-04-23 9:15:00', 'barbaz'), 
(001, '2012-04-23 9:15:01', 'barbaz'), 
(002, '2012-04-24 3:41:01', 'foo'), 
(002, '2012-04-24 3:41:03', 'foo') 
; 

Trả lời

6

Lấy cảm hứng từ this answer, được quay trở lại trước @a_horse_with_no_name.

WITH groupped_t AS (
SELECT *, sum(grp_id) OVER (ORDER BY userid,product,"timestamp") AS grp_nr 
    FROM (SELECT t.*, 
      lag("timestamp") OVER 
      (PARTITION BY userid,product ORDER BY "timestamp") AS prev_ts, 
      CASE WHEN ("timestamp" - lag("timestamp") OVER 
      (PARTITION BY userid,product ORDER BY "timestamp")) <= '2s'::interval 
      THEN NULL ELSE 1 END AS grp_id 
     FROM t) AS g 
), periods AS (
SELECT min(gt."timestamp") AS grp_min, max(gt."timestamp") AS grp_max, grp_nr 
    FROM groupped_t AS gt 
GROUP BY gt.grp_nr 
) 
SELECT gt.userid, p.grp_min AS "begin", p.grp_max AS "end", gt.product 
    FROM periods p 
    JOIN groupped_t gt ON gt.grp_nr = p.grp_nr AND gt."timestamp" = p.grp_min 
ORDER BY gt.userid, p.grp_min; 
  1. Truy vấn trong cùng sẽ gán groupping ID dựa trên sự khác biệt userid, product và thời gian. Tôi cho rằng nó sẽ an toàn cho PARTITION BY hai trường đầu tiên trên thực tế.
  2. groupped_t cung cấp cho tôi tất cả các cột nguồn + số nhóm chạy bổ sung. Tôi chỉ sử dụng ORDER BY ở đây cho chức năng cửa sổ sum(), vì tôi cần ID nhóm là duy nhất.
  3. periods chỉ là truy vấn trợ giúp cho dấu thời gian đầu tiên và cuối cùng trong mỗi nhóm.
  4. Cuối cùng, tôi tham gia groupped_t với periods trên grp_nr (đó là lý do tại sao tôi cần nó là duy nhất) và dấu thời gian của mục nhập đầu tiên trong mỗi nhóm.

Bạn cũng có thể kiểm tra truy vấn này trên SQL Fiddle.

Lưu ý, rằng timestamp, beginendreserved words in the SQL (end cũng cho PostgreSQL), vì vậy bạn nên tránh hoặc một trong hai đúp trích họ.

+0

Đó là ... đẹp! Hoạt động chính xác như dự định, cảm ơn! –

Các vấn đề liên quan