2012-06-25 15 views
6

मेरे पास एक सारणी है जो एक उत्पाद के उपयोग का प्रतिनिधित्व करती है, जैसे कि लॉग की तरह। उत्पाद उपयोग एकाधिक टाइमस्टैम्प के रूप में लॉग किया गया है, मैं समय सीमा का उपयोग कर एक ही डेटा का प्रतिनिधित्व करना चाहता हूं।संगत टाइमस्टैम्प वाले कई पंक्तियों को संकुचित करें

यह इस तरह दिखता है (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 

मैं पंक्तियों जिसका समय पिछले रन के साथ अंतर एक डेल्टा (2 सेकंड कहते हैं) से कम है संक्षिप्त करने के लिए चाहते हैं समय और समाप्ति समय शुरू करते हैं, इस तरह:

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 

कृपया ध्यान दें एक ही उत्पाद की कि लगातार उपयोग दो पंक्तियों में विभाजित है उनके उपयोग से अधिक डेल है टा (2 सेकंड, इस उदाहरण में) अलग।

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') 
; 

उत्तर

6

, this answer से प्रेरित होकर एक समय पहले @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. अंतरतम क्वेरी userid, product और समय के अंतर के आधार आईडी groupping आवंटित करेगा। मुझे लगता है कि यह वास्तव में PARTITION BY पहले दो क्षेत्रों में सुरक्षित होना चाहिए।
  2. groupped_t मुझे सभी स्रोत कॉलम + एक अतिरिक्त चल रहे समूह संख्या देता है। मैंने sum() विंडो फ़ंक्शन के लिए केवल ORDER BY का उपयोग किया, क्योंकि मुझे समूह आईडी अद्वितीय होने की आवश्यकता है।
  3. periods प्रत्येक समूह में पहले और अंतिम टाइमस्टैम्प के लिए केवल एक सहायक क्वेरी है।
  4. अंत में, grp_nr पर periods के साथ groupped_t में शामिल हो गया (इसलिए मुझे इसे अद्वितीय होने की आवश्यकता है) और प्रत्येक समूह में पहली प्रविष्टि का टाइमस्टैम्प।

आप यह प्रश्न SQL Fiddle पर भी देख सकते हैं।

ध्यान दें, कि timestamp, begin और endreserved words in the SQL (end भी PostgreSQL के लिए) कर रहे हैं, तो आप या तो बचना चाहिए या उन्हें डबल बोली।

+0

यह ... सुंदर है! बिल्कुल इरादे के रूप में काम करता है, धन्यवाद! –

संबंधित मुद्दे