समूह या खिड़की


13

मेरे पास एक ऐसी स्थिति है जो मुझे लगता है कि विंडो फ़ंक्शन का उपयोग करके हल किया जा सकता है लेकिन मुझे यकीन नहीं है।

निम्न तालिका की कल्पना करें

CREATE TABLE tmp
  ( date timestamp,        
    id_type integer
  ) ;

INSERT INTO tmp 
    ( date, id_type )
VALUES
    ( '2017-01-10 07:19:21.0', 3 ),
    ( '2017-01-10 07:19:22.0', 3 ),
    ( '2017-01-10 07:19:23.1', 3 ),
    ( '2017-01-10 07:19:24.1', 3 ),
    ( '2017-01-10 07:19:25.0', 3 ),
    ( '2017-01-10 07:19:26.0', 5 ),
    ( '2017-01-10 07:19:27.1', 3 ),
    ( '2017-01-10 07:19:28.0', 5 ),
    ( '2017-01-10 07:19:29.0', 5 ),
    ( '2017-01-10 07:19:30.1', 3 ),
    ( '2017-01-10 07:19:31.0', 5 ),
    ( '2017-01-10 07:19:32.0', 3 ),
    ( '2017-01-10 07:19:33.1', 5 ),
    ( '2017-01-10 07:19:35.0', 5 ),
    ( '2017-01-10 07:19:36.1', 5 ),
    ( '2017-01-10 07:19:37.1', 5 )
  ;

मैं कॉलम id_type पर प्रत्येक परिवर्तन पर एक नया समूह रखना चाहूंगा। ईजी 1 समूह 7:19:21 से 7:19:25 तक, दूसरा शुरू और परिष्करण 7:19:26, और इसी तरह।
यह काम करने के बाद, मैं समूहों को परिभाषित करने के लिए और अधिक मापदंड शामिल करना चाहता हूं।

इस समय, नीचे दिए गए प्रश्न का उपयोग करके ...

SELECT distinct 
    min(min(date)) over w as begin, 
    max(max(date)) over w as end,   
    id_type
from tmp
GROUP BY id_type
WINDOW w as (PARTITION BY id_type)
order by  begin;

मुझे निम्नलिखित परिणाम मिले:

begin                   end                     id_type
2017-01-10 07:19:21.0   2017-01-10 07:19:32.0   3
2017-01-10 07:19:26.0   2017-01-10 07:19:37.1   5

जबकि मैं चाहूंगा:

begin                   end                     id_type
2017-01-10 07:19:21.0   2017-01-10 07:19:25.0   3
2017-01-10 07:19:26.0   2017-01-10 07:19:26.0   5
2017-01-10 07:19:27.1   2017-01-10 07:19:27.1   3
2017-01-10 07:19:28.0   2017-01-10 07:19:29.0   5
2017-01-10 07:19:30.1   2017-01-10 07:19:30.1   3
2017-01-10 07:19:31.0   2017-01-10 07:19:31.0   5
2017-01-10 07:19:32.0   2017-01-10 07:19:32.0   3
2017-01-10 07:19:33.1   2017-01-10 07:19:37.1   5

जब मैं इस पहले चरण को हल कर लेता हूं, तो मैं समूहों को तोड़ने के लिए नियमों के रूप में उपयोग करने के लिए और कॉलम जोड़ूंगा, और ये अन्य अशक्त होंगे।

Postgres Version: 8.4 (हमारे पास Postgis के साथ Postgres हैं, इसलिए इसे अपग्रेड करना आसान नहीं है। Postgis फ़ंक्शंस में नाम बदल जाते हैं और अन्य समस्याएं हैं, लेकिन उम्मीद है कि हम पहले से ही सब कुछ लिख रहे हैं और नया संस्करण एक नए वर्जन X का उपयोग करेगा) पोस्टगिस 2.x)


2
सामान्य समाधान: dba.stackexchange.com/questions/35380/…
Erwin Brandstetter

जवाबों:


4

कुछ बिंदुओं के लिए,

  • एक गैर-अस्थायी तालिका को कॉल न करें tmpजो बस भ्रमित हो जाती है।
  • टाइमस्टैम्प के लिए पाठ का उपयोग न करें (आप ऐसा कर रहे हैं कि आपके उदाहरण में हम यह बता सकते हैं क्योंकि टाइमस्टैम्प छोटा नहीं हुआ है और है .0)
  • उस फ़ील्ड को कॉल न करें जिसमें समय हो date। यदि इसकी तिथि और समय है, तो यह टाइमस्टैम्प है (और इसे एक के रूप में संग्रहीत करें)

एक खिड़की समारोह का उपयोग करने के लिए बेहतर ..

SELECT id_type, grp, min(date), max(date)
FROM (
  SELECT date, id_type, count(is_reset) OVER (ORDER BY date) AS grp
  FROM (
    SELECT date, id_type, CASE WHEN lag(id_type) OVER (ORDER BY date) <> id_type THEN 1 END AS is_reset
    FROM tmp
  ) AS t
) AS g
GROUP BY id_type, grp
ORDER BY min(date);

आउटपुट

 id_type | grp |          min          |          max          
---------+-----+-----------------------+-----------------------
       3 |   0 | 2017-01-10 07:19:21.0 | 2017-01-10 07:19:25.0
       5 |   1 | 2017-01-10 07:19:26.0 | 2017-01-10 07:19:26.0
       3 |   2 | 2017-01-10 07:19:27.1 | 2017-01-10 07:19:27.1
       5 |   3 | 2017-01-10 07:19:28.0 | 2017-01-10 07:19:29.0
       3 |   4 | 2017-01-10 07:19:30.1 | 2017-01-10 07:19:30.1
       5 |   5 | 2017-01-10 07:19:31.0 | 2017-01-10 07:19:31.0
       3 |   6 | 2017-01-10 07:19:32.0 | 2017-01-10 07:19:32.0
       5 |   7 | 2017-01-10 07:19:33.1 | 2017-01-10 07:19:37.1
(8 rows)

explaination

पहले हमें रीसेट की आवश्यकता है .. हम उन्हें उत्पन्न करते हैं lag()

SELECT date, id_type, CASE WHEN lag(id_type) OVER (ORDER BY date) <> id_type THEN 1 END AS is_reset
FROM tmp
ORDER BY date;

         date          | id_type | is_reset 
-----------------------+---------+----------
 2017-01-10 07:19:21.0 |       3 |         
 2017-01-10 07:19:22.0 |       3 |         
 2017-01-10 07:19:23.1 |       3 |         
 2017-01-10 07:19:24.1 |       3 |         
 2017-01-10 07:19:25.0 |       3 |         
 2017-01-10 07:19:26.0 |       5 |        1
 2017-01-10 07:19:27.1 |       3 |        1
 2017-01-10 07:19:28.0 |       5 |        1
 2017-01-10 07:19:29.0 |       5 |         
 2017-01-10 07:19:30.1 |       3 |        1
 2017-01-10 07:19:31.0 |       5 |        1
 2017-01-10 07:19:32.0 |       3 |        1
 2017-01-10 07:19:33.1 |       5 |        1
 2017-01-10 07:19:35.0 |       5 |         
 2017-01-10 07:19:36.1 |       5 |         
 2017-01-10 07:19:37.1 |       5 |         
(16 rows)

फिर हम समूह प्राप्त करने के लिए गिनते हैं।

SELECT date, id_type, count(is_reset) OVER (ORDER BY date) AS grp
FROM (
  SELECT date, id_type, CASE WHEN lag(id_type) OVER (ORDER BY date) <> id_type THEN 1 END AS is_reset
  FROM tmp
  ORDER BY date
) AS t
ORDER BY date

         date          | id_type | grp 
-----------------------+---------+-----
 2017-01-10 07:19:21.0 |       3 |   0
 2017-01-10 07:19:22.0 |       3 |   0
 2017-01-10 07:19:23.1 |       3 |   0
 2017-01-10 07:19:24.1 |       3 |   0
 2017-01-10 07:19:25.0 |       3 |   0
 2017-01-10 07:19:26.0 |       5 |   1
 2017-01-10 07:19:27.1 |       3 |   2
 2017-01-10 07:19:28.0 |       5 |   3
 2017-01-10 07:19:29.0 |       5 |   3
 2017-01-10 07:19:30.1 |       3 |   4
 2017-01-10 07:19:31.0 |       5 |   5
 2017-01-10 07:19:32.0 |       3 |   6
 2017-01-10 07:19:33.1 |       5 |   7
 2017-01-10 07:19:35.0 |       5 |   7
 2017-01-10 07:19:36.1 |       5 |   7
 2017-01-10 07:19:37.1 |       5 |   7
(16 rows)

फिर हम एक सबसिले में लपेटते हैं GROUP BYऔर ORDERन्यूनतम अधिकतम (सीमा) का चयन करते हैं

SELECT id_type, grp, min(date), max(date)
FROM (
  .. stuff
) AS g
GROUP BY id_type, grp
ORDER BY min(date);

16

1. विंडो फ़ंक्शंस प्लस सबक्वेरीज़

संशोधन और सुधार के साथ इवान के विचार के समान समूह बनाने के लिए चरणों की गणना करें :

SELECT id_type
     , min(date) AS begin
     , max(date) AS end
     , count(*)  AS row_ct  -- optional addition
FROM  (
   SELECT date, id_type, count(step OR NULL) OVER (ORDER BY date) AS grp
   FROM  (
      SELECT date, id_type
           , lag(id_type, 1, id_type) OVER (ORDER BY date) <> id_type AS step
      FROM   tmp
      ) sub1
   ) sub2
GROUP  BY id_type, grp
ORDER  BY min(date);

यह मान लिया गया है कि कॉलम शामिल हैं NOT NULL। और आपको अधिक करने की आवश्यकता है।

इसके अलावा dateपरिभाषित किया जा रहा है UNIQUE, अन्यथा आप ORDER BYखंडों के लिए एक टाईब्रेकर जोड़ने की जरूरत है निर्धारक परिणाम प्राप्त करते हैं। जैसा: ORDER BY date, id

विस्तृत विवरण (बहुत समान प्रश्न का उत्तर):

विशेष रूप से ध्यान दें:

  • संबंधित मामलों में, lag()3 मापदंडों के साथ पहले (या अंतिम) पंक्ति के कोने मामले को सुरुचिपूर्ण ढंग से कवर करने के लिए आवश्यक हो सकता है। (यदि कोई पिछली (अगली) पंक्ति नहीं है तो 3rd परम का उपयोग डिफ़ॉल्ट के रूप में किया जाता है।

    lag(id_type, 1, id_type) OVER ()

    जब से हम केवल एक वास्तविक में रुचि रखने वाले कर रहे हैं परिवर्तन के id_type( TRUE), यह इस विशेष मामले में कोई फर्क नहीं पड़ता। NULLऔर FALSEदोनों की गिनती नहीं है step

  • count(step OR NULL) OVER (ORDER BY date)सबसे छोटा वाक्यविन्यास है जो पोस्टग्रेज 9.3 या पुराने में भी काम करता है। count()केवल गैर-शून्य मान गिनता है ...

    आधुनिक पोस्टग्रेज में, क्लीनर, समकक्ष सिंटैक्स होगा:

    count(step) FILTER (WHERE step) OVER (ORDER BY date)

    विवरण:

2. दो विंडो फ़ंक्शन, एक सबक्वेरी को घटाएं

संशोधनों के साथ एरिक के विचार के समान :

SELECT min(date) AS begin
     , max(date) AS end
     , id_type
FROM  (
   SELECT date, id_type
        , row_number() OVER (ORDER BY date)
        - row_number() OVER (PARTITION BY id_type ORDER BY date) AS grp
   FROM   tmp
   ) sub
GROUP  BY id_type, grp
ORDER  BY min(date);

यदि dateपरिभाषित किया गया है UNIQUE, जैसे कि मैंने ऊपर उल्लेख किया है (आपने कभी स्पष्ट नहीं किया है), dense_rank()व्यर्थ होगा, क्योंकि परिणाम के लिए समान है row_number()और बाद वाला काफी सस्ता है।

तो dateहै नहीं परिभाषित UNIQUE(और हम नहीं जानते कि केवल डुप्लिकेट पर हैं (date, id_type)), इन प्रश्नों के सभी व्यर्थ कर रहे हैं, के बाद से परिणाम मनमाना है।

इसके अलावा, एक उपश्रेणी आमतौर पर पोस्टग्रेज में सीटीई से सस्ती होती है। जब आपको उनकी आवश्यकता हो तो केवल सीटीई का उपयोग करें।

अधिक स्पष्टीकरण के साथ संबंधित जवाब:

संबंधित मामलों में जहां हमारे पास पहले से ही तालिका में संख्या चल रही है, हम एक एकल विंडो फ़ंक्शन के साथ कर सकते हैं:

3. plpgsql फ़ंक्शन के साथ शीर्ष प्रदर्शन

चूंकि यह प्रश्न अप्रत्याशित रूप से लोकप्रिय हो गया है, इसलिए मैं शीर्ष प्रदर्शन को प्रदर्शित करने के लिए एक और समाधान जोड़ूंगा।

SQL में छोटे और सुरुचिपूर्ण सिंटैक्स के साथ समाधान बनाने के लिए कई परिष्कृत उपकरण हैं। लेकिन एक घोषणात्मक भाषा में अधिक जटिल आवश्यकताओं के लिए अपनी सीमाएं होती हैं जिनमें प्रक्रियात्मक तत्व शामिल होते हैं।

एक सर्वर-साइड प्रक्रियात्मक फ़ंक्शन इसके लिए अब तक पोस्ट की गई किसी भी चीज़ की तुलना में तेज़ है क्योंकि इसे केवल टेबल पर एक एकल अनुक्रमिक स्कैन और एक एकल प्रकार के ऑपरेशन की आवश्यकता है । यदि एक फिटिंग इंडेक्स उपलब्ध है, तो भी केवल एक इंडेक्स-केवल स्कैन।

CREATE OR REPLACE FUNCTION f_tmp_groups()
  RETURNS TABLE (id_type int, grp_begin timestamp, grp_end timestamp) AS
$func$
DECLARE
   _row  tmp;                       -- use table type for row variable
BEGIN
   FOR _row IN
      TABLE tmp ORDER BY date       -- add more columns to make order deterministic
   LOOP
      CASE _row.id_type = id_type 
      WHEN TRUE THEN                -- same group continues
         grp_end := _row.date;      -- remember last date so far
      WHEN FALSE THEN               -- next group starts
         RETURN NEXT;               -- return result for last group
         id_type   := _row.id_type;
         grp_begin := _row.date;
         grp_end   := _row.date;
      ELSE                          -- NULL for 1st row
         id_type   := _row.id_type; -- remember row data for starters
         grp_begin := _row.date;
         grp_end   := _row.date;
      END CASE;
   END LOOP;

   RETURN NEXT;                     -- return last result row      
END
$func$ LANGUAGE plpgsql;

कॉल करें:

SELECT * FROM f_tmp_groups();

इसके साथ टेस्ट करें:

EXPLAIN (ANALYZE, TIMING OFF)  -- to focus on total performance
SELECT * FROM  f_tmp_groups();

आप बहुरूप प्रकारों और तालिका प्रकार और स्तंभ नामों के साथ कार्य को सामान्य बना सकते हैं। विवरण:

यदि आप इसके लिए कोई कार्य नहीं करना चाहते हैं या नहीं कर सकते हैं, तो यह फ्लाई पर एक अस्थायी फ़ंक्शन बनाने के लिए भी भुगतान करेगा। कुछ एमएस खर्च होता है।


पोस्टग्रैस 9.6 के लिए dbfiddle , तीनों केप्रदर्शन की तुलना करते हुए।जैक के परीक्षण मामले परसंशोधित, संशोधित।

Postgres 8.4 के लिए dbfiddle , जहां प्रदर्शन अंतर और भी बड़ा है।


इसे कुछ समय पढ़ें - अभी भी अनिश्चित है कि आप तीन तर्क अंतराल के बारे में क्या बात कर रहे हैं या जब आपको इसका उपयोग करना होगा count(x or null)या यहां तक ​​कि यह क्या कर रहा है। शायद आप कुछ नमूने जहां यह दिखा सकता है , की आवश्यकता है क्योंकि इसे यहाँ आवश्यक नहीं है। और, उन कोने के मामलों को कवर करने के लिए क्या आवश्यकता होगी। BTW, मैं सिर्फ pl / pgsql उदाहरण के लिए अपने downvote को upvote में बदल दिया। यह वास्तव में अच्छा है। (लेकिन, आम तौर पर मैं उन उत्तरों के खिलाफ होता हूं जो अन्य उत्तरों को संक्षिप्त करते हैं या कोने के मामलों को कवर करते हैं - हालांकि मुझे यह कहने से नफरत है कि यह एक कोने का मामला है क्योंकि मैं इसे नहीं समझता हूं)।
इवान कैरोल

मैं उन्हें दो अलग-अलग स्व-उत्तर वाले प्रश्नों में डालूंगा क्योंकि मुझे यकीन है कि मैं सोच नहीं पा रहा हूं कि क्या count(x or null)करता है। यदि आप चाहें तो मुझे दोनों प्रश्न पूछने में खुशी होगी।
इवान कैरोल


7

आप इसे ROW_NUMBER()संचालन के एक साधारण घटाव के रूप में कर सकते हैं (या यदि आपकी तिथियां अद्वितीय नहीं हैं, हालांकि अभी भी अद्वितीय हैं id_type, तो आप DENSE_RANK()इसके बजाय उपयोग कर सकते हैं , हालांकि यह अधिक महंगी क्वेरी होगी):

WITH IdTypes AS (
   SELECT
      date,
      id_type,
      Row_Number() OVER (ORDER BY date)
         - Row_Number() OVER (PARTITION BY id_type ORDER BY date)
         AS Seq
   FROM
      tmp
)
SELECT
   Min(date) AS begin,
   Max(date) AS end,
   id_type
FROM IdTypes
GROUP BY id_type, Seq
ORDER BY begin
;

DB Fiddle में यह काम देखें (या DENSE_RANK संस्करण देखें )

परिणाम:

begin                  end                    id_type
---------------------  ---------------------  -------
2017-01-10 07:19:21    2017-01-10 07:19:25    3
2017-01-10 07:19:26    2017-01-10 07:19:26    5
2017-01-10 07:19:27.1  2017-01-10 07:19:27.1  3
2017-01-10 07:19:28    2017-01-10 07:19:29    5
2017-01-10 07:19:30.1  2017-01-10 07:19:30.1  3
2017-01-10 07:19:31    2017-01-10 07:19:31    5
2017-01-10 07:19:32    2017-01-10 07:19:32    3
2017-01-10 07:19:33.1  2017-01-10 07:19:37.1  5

तार्किक रूप से, आप एक सरल रूप में इस के बारे में सोच सकते हैं DENSE_RANK()एक साथ PREORDER BY, यह है कि, आप चाहते हैं DENSE_RANKसभी आइटम है कि एक साथ दिया जाता है इसके, और आप चाहते हैं उन्हें दिनांक द्वारा आदेश दिया, तो आप सिर्फ तथ्य यह है कि के परेशान करने की समस्या से निपटने के लिए तिथि में प्रत्येक परिवर्तन पर, DENSE_RANKवेतन वृद्धि होगी। आप अभिव्यक्ति का उपयोग करके जैसा कि मैंने आपको ऊपर दिखाया है। कल्पना करें कि आपके पास यह सिंटैक्स था: DENSE_RANK() OVER (PREORDER BY date, ORDER BY id_type)जहां PREORDERरैंकिंग गणना से बाहर रखा गया है और केवल ORDER BYगिना जाता है।

ध्यान दें कि यह GROUP BYदोनों उत्पन्न Seqकॉलम के साथ-साथ कॉलम के लिए भी महत्वपूर्ण है id_typeSeqअपने आप में अद्वितीय नहीं है, ओवरलैप हो सकते हैं - आपको समूह भी होना चाहिए id_type

इस विषय पर आगे पढ़ने के लिए:

यह पहला लिंक आपको कुछ कोड देता है जिनका आप उपयोग कर सकते हैं यदि आप चाहते थे कि आरंभ या समाप्ति तिथि पिछली या अगली अवधि की समाप्ति / आरंभ तिथि के समान हो (ताकि कोई अंतराल न हो)। प्लस अन्य संस्करण जो आपकी क्वेरी में आपकी सहायता कर सकते हैं। हालाँकि उन्हें SQL सर्वर सिंटैक्स से अनुवादित किया जाना है ...


6

Postgres 8.4 पर आप एक RECURSIVE फ़ंक्शन का उपयोग कर सकते हैं ।

वह यह कैसे करते हैं

पुनरावर्ती फ़ंक्शन प्रत्येक भिन्न id_type में एक स्तर जोड़ता है, दिनांक को एक-एक करके अवरोही क्रम पर चुनता है।

       date           | id_type | lv
--------------------------------------
2017-01-10 07:19:21.0      3       8
2017-01-10 07:19:22.0      3       8
2017-01-10 07:19:23.1      3       8
2017-01-10 07:19:24.1      3       8
2017-01-10 07:19:25.0      3       8
2017-01-10 07:19:26.0      5       7
2017-01-10 07:19:27.1      3       6
2017-01-10 07:19:28.0      5       5
2017-01-10 07:19:29.0      5       5
2017-01-10 07:19:30.1      3       4
2017-01-10 07:19:31.0      5       3
2017-01-10 07:19:32.0      3       2
2017-01-10 07:19:33.1      5       1
2017-01-10 07:19:35.0      5       1
2017-01-10 07:19:36.1      5       1
2017-01-10 07:19:37.1      5       1

तब वांछित परिणाम प्राप्त करने के लिए MAX (दिनांक), MIN (दिनांक) स्तर के आधार पर समूहीकरण, id_type का उपयोग करें।

with RECURSIVE rdates as 
(
    (select   date, id_type, 1 lv 
     from     yourTable
     order by date desc
     limit 1
    )
    union
    (select    d.date, d.id_type,
               case when r.id_type = d.id_type 
                    then r.lv 
                    else r.lv + 1 
               end lv    
    from       yourTable d
    inner join rdates r
    on         d.date < r.date
    order by   date desc
    limit      1)
)
select   min(date) StartDate,
         max(date) EndDate,
         id_type
from     rdates
group by lv, id_type
;

+---------------------+---------------------+---------+
| startdate           |       enddate       | id_type |
+---------------------+---------------------+---------+
| 10.01.2017 07:19:21 | 10.01.2017 07:19:25 |    3    |
| 10.01.2017 07:19:26 | 10.01.2017 07:19:26 |    5    |
| 10.01.2017 07:19:27 | 10.01.2017 07:19:27 |    3    |
| 10.01.2017 07:19:28 | 10.01.2017 07:19:29 |    5    |
| 10.01.2017 07:19:30 | 10.01.2017 07:19:30 |    3    |
| 10.01.2017 07:19:31 | 10.01.2017 07:19:31 |    5    |
| 10.01.2017 07:19:32 | 10.01.2017 07:19:32 |    3    |
| 10.01.2017 07:19:33 | 10.01.2017 07:19:37 |    5    |
+---------------------+---------------------+---------+

इसे देखें: http://rextester.com/WCOYFP6623


5

यहां एक और तरीका है, जो इवान और इरविन के समान है, यह द्वीपों को निर्धारित करने के लिए एलएजी का उपयोग करता है। यह उन समाधानों से अलग है जिसमें यह केवल एक स्तर के घोंसले के शिकार का उपयोग करता है, कोई समूहीकरण नहीं करता है, और बहुत अधिक खिड़की कार्य करता है:

SELECT
  id_type,
  date AS begin,
  COALESCE(
    LEAD(prev_date) OVER (ORDER BY date ASC),
    last_date
  ) AS end
FROM
  (
    SELECT
      id_type,
      date,
      LAG(date) OVER (ORDER BY date ASC) AS prev_date,
      MAX(date) OVER () AS last_date,
      CASE id_type
        WHEN LAG(id_type) OVER (ORDER BY date ASC)
        THEN 0
        ELSE 1
      END AS is_start
    FROM
      tmp
  ) AS derived
WHERE
  is_start = 1
ORDER BY
  date ASC
;

is_startनेस्टेड का चयन करें चिह्नों में गणना स्तंभ प्रत्येक द्वीप की शुरुआत। इसके अतिरिक्त, नेस्टेड SELECT प्रत्येक पंक्ति की पिछली तिथि और डेटासेट की अंतिम तिथि को उजागर करता है।

उन पंक्तियों के लिए जो उनके संबंधित द्वीपों की शुरुआत हैं, पिछली तारीख प्रभावी रूप से पिछले द्वीप की समाप्ति तिथि है। यही कारण है कि मुख्य चयन इसका उपयोग करता है। यह केवल पंक्तियों को is_start = 1स्थिति से मेल खाता है , और प्रत्येक लौटी हुई पंक्ति के लिए यह पंक्ति के dateरूप में beginऔर निम्न पंक्ति के prev_dateरूप में दिखाता है end। चूंकि अंतिम पंक्ति में निम्न पंक्ति नहीं होती है, LEAD(prev_date)इसलिए इसके लिए एक अशक्त रिटर्न देता है, जिसके लिए COALESCE फ़ंक्शन डेटासेट की अंतिम तिथि को प्रतिस्थापित करता है।

आप dbfiddle पर इस समाधान के साथ खेल सकते हैं ।

द्वीपों की पहचान करने वाले अतिरिक्त स्तंभों को शुरू करते समय, आप संभवतः प्रत्येक विंडो फ़ंक्शन के OOS क्लॉज के लिए उपखंड द्वारा एक भाग प्रस्तुत करना चाहेंगे। उदाहरण के लिए, यदि आप द्वारा परिभाषित समूहों के भीतर द्वीपों का पता लगाना चाहते हैं parent_id, तो उपरोक्त क्वेरी को संभवतः इस तरह देखना होगा:

SELECT
  parent_id,
  id_type,
  date AS begin,
  COALESCE(
    LEAD(prev_date) OVER (PARTITION BY parent_id ORDER BY date ASC),
    last_date
  ) AS end
FROM
  (
    SELECT
      parent_id,
      id_type,
      date,
      LAG(date) OVER (PARTITION BY parent_id ORDER BY date ASC) AS prev_date,
      MAX(date) OVER (PARTITION BY parent_id) AS last_date,
      CASE id_type
        WHEN LAG(id_type) OVER (PARTITION BY parent_id ORDER BY date ASC)
        THEN 0
        ELSE 1
      END AS is_start
    FROM
      tmp
  ) AS derived
WHERE
  is_start = 1
ORDER BY
  date ASC
;

और यदि आप इरविन या इवान के समाधान के साथ जाने का फैसला करते हैं, तो मेरा मानना ​​है कि इसी तरह के बदलाव को भी इसके साथ जोड़ना होगा।


5

एक व्यावहारिक समाधान के रूप में शैक्षणिक रुचि से अधिक, आप इसे उपयोगकर्ता द्वारा परिभाषित कुल के साथ भी प्राप्त कर सकते हैं । अन्य समाधानों की तरह, यह भी Postgres 8.4 पर काम करेगा, लेकिन जैसा कि अन्य ने टिप्पणी की है, कृपया अपग्रेड करें यदि आप कर सकते हैं।

सकल संभालता है nullजैसे कि यह एक अलग है foo_type, इसलिए नल के रन को समान दिया जाएगा grp- जो आप चाहते हैं या नहीं हो सकता है।

create function grp_sfunc(integer[],integer) returns integer[] language sql as $$
  select array[$1[1]+($1[2] is distinct from $2 or $1[3]=0)::integer,$2,1];
$$;
create function grp_finalfunc(integer[]) returns integer language sql as $$
  select $1[1];
$$;
create aggregate grp(integer)(
  sfunc = grp_sfunc
, stype = integer[]
, finalfunc = grp_finalfunc
, initcond = '{0,0,0}'
);
select min(foo_at) begin_at, max(foo_at) end_at, foo_type
from (select *, grp(foo_type) over (order by foo_at) from foo) z
group by grp, foo_type
order by 1;
start_at | अंत_त | foo_type
: -------------------- | : -------------------- | -------:
2017-01-10 07:19:21 | 2017-01-10 07:19:25 | 3
2017-01-10 07:19:26 | 2017-01-10 07:19:26 | 5
2017-01-10 07: 19: 27.1 | 2017-01-10 07: 19: 27.1 | 3
2017-01-10 07:19:28 | 2017-01-10 07:19:29 | 5
2017-01-10 07: 19: 30.1 | 2017-01-10 07: 19: 30.1 | 3
2017-01-10 07:19:31 | 2017-01-10 07:19:31 | 5
2017-01-10 07:19:32 | 2017-01-10 07:19:32 | 3
2017-01-10 07: 19: 33.1 | 2017-01-10 07: 19: 37.1 | 5

यहाँ dbfiddle


4

यह RECURSIVE CTEएक पंक्ति से दूसरी पंक्ति में "शुरुआती समय" को पारित करने के लिए किया जा सकता है , और कुछ अतिरिक्त (सुविधा) तैयारियां।

यह क्वेरी आपके इच्छित परिणाम देता है:

WITH RECURSIVE q AS
(
    SELECT
        id_type,
        "date",
        /* We compute next id_type for convenience, plus row_number */
        row_number()  OVER (w) AS rn,
        lead(id_type) OVER (w) AS next_id_type
    FROM
        t
    WINDOW
        w AS (ORDER BY "date") 
)

तैयारी के बाद ... पुनरावर्ती भाग

, rec AS 
(
    /* Anchor */
    SELECT
        q.rn,
        q."date" AS "begin",
        /* When next_id_type is different from Look also at **next** row to find out whether we need to mark an end */
        case when q.id_type is distinct from q.next_id_type then q."date" END AS "end",
        q.id_type
    FROM
        q
    WHERE
        rn = 1

    UNION ALL

    /* Loop */
    SELECT
        q.rn,
        /* We keep copying 'begin' from one row to the next while type doesn't change */
        case when q.id_type = rec.id_type then rec.begin else q."date" end AS "begin",
        case when q.id_type is distinct from q.next_id_type then q."date" end AS "end",
        q.id_type
    FROM
        rec
        JOIN q ON q.rn = rec.rn+1
)
-- We filter the rows where "end" is not null, and project only needed columns
SELECT
    "begin", "end", id_type
FROM
    rec
WHERE
    "end" is not null ;

आप इसे http://rextester.com/POYM83542 पर देख सकते हैं

यह तरीका अच्छा नहीं है। 8_641 पंक्ति तालिका के लिए, यह 7s लेता है, तालिका के लिए दो बार उस आकार में, यह 28s लेता है। कुछ नमूने O (n ^ 2) की तरह दिखने वाले निष्पादन समय को दिखाते हैं।

इवान कैरोल की विधि 1s (यानी: इसके लिए जाओ!) से कम समय लेती है, और O (n) की तरह लगती है। पुनरावर्ती प्रश्न बिल्कुल अक्षम हैं, और इसे अंतिम उपाय माना जाना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.