দলবদ্ধকরণ বা উইন্ডো


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

আইডি_ টাইপ কলামে প্রতিটি পরিবর্তনে আমি একটি নতুন গ্রুপ রাখতে চাই। EG ম গ্রুপটি 7:19:21 থেকে 7:19:25, 2 য় শুরু এবং 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

আমি এই প্রথম পদক্ষেপটি সমাধান করার পরে, গ্রুপগুলি ভাঙ্গার নিয়ম হিসাবে আমি আরও কলাম যুক্ত করব এবং এই অন্যান্যগুলি আবশ্যক।

পোস্টগ্রিজ সংস্করণ: 8.4 (আমাদের পোস্টগ্রিসের সাথে পোস্টগ্রিস রয়েছে, তাই এটি আপগ্রেড করা সহজ নয় Post পোস্টগ্রিস ফাংশনগুলির নাম পরিবর্তন হয় এবং অন্যান্য সমস্যা রয়েছে, তবে আশা করি আমরা ইতিমধ্যে সমস্ত কিছু লিখছি এবং নতুন সংস্করণটি নতুন সংস্করণটি 9.X ব্যবহার করবে postgis 2.x)


উত্তর:


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)

তারপর আমরা subselect মধ্যে মোড়ানো GROUP BYএবং ORDERএবং কমপক্ষে MAX (পরিসীমা) নির্বাচন

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ধারাগুলিতে টাইব্রেকার যুক্ত করা দরকার ডিটারমিনিস্টিক ফলাফল get ভালো লেগেছে: ORDER BY date, id

বিস্তারিত ব্যাখ্যা (খুব অনুরূপ প্রশ্নের উত্তর):

বিশেষত দ্রষ্টব্য:

  • সম্পর্কিত ক্ষেত্রে, lag()3 পরামিতি সহ প্রথম (বা শেষ) সারিটির কর্নার কেসটি মার্জিতভাবে আবরণ করার জন্য প্রয়োজনীয় হতে পারে। (3 য় প্যারাম পূর্ববর্তী (পরবর্তী) সারি না থাকলে ডিফল্ট হিসাবে ব্যবহৃত হয়।

    lag(id_type, 1, id_type) OVER ()

    যেহেতু আমরা কেবলমাত্র ( ) এর আসল পরিবর্তনে আগ্রহী তাই এই বিশেষ ক্ষেত্রে এটি কোনও ব্যাপার নয়। এবং উভয় হিসাবে গণনা করা হয় না ।id_typeTRUENULLFALSEstep

  • count(step OR NULL) OVER (ORDER BY date)সবচেয়ে সংক্ষিপ্ত বাক্য গঠন যা পোস্টগ্রিস 9.3 বা তার বেশি পুরানো ক্ষেত্রেও কাজ করে। count()কেবল অ-নাল মান গণনা করে ...

    আধুনিক পোস্টগ্রিসে, ক্লিনার, সমতুল্য বাক্য গঠনটি হ'ল:

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

    বিবরণ:

২. দুটি উইন্ডো ফাংশন বিয়োগ করুন, একটি উপশক্তি

অনুরূপ এরিক এর ধারণা সংশোধনসহ:

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 ফাংশন সঙ্গে শীর্ষ কর্মক্ষমতা

যেহেতু এই প্রশ্নটি অপ্রত্যাশিতভাবে জনপ্রিয় হয়ে উঠেছে, আমি শীর্ষ সম্পাদনা প্রদর্শনের জন্য আরও একটি সমাধান যুক্ত করব।

সংক্ষিপ্ত এবং মার্জিত সিনট্যাক্স সহ সমাধানগুলি তৈরি করতে এসকিউএল এর কাছে অনেক পরিশীলিত সরঞ্জাম রয়েছে। তবে একটি ঘোষিত ভাষার আরও জটিল প্রয়োজনীয়তার জন্য সীমা থাকে যা পদ্ধতিগত উপাদানগুলিকে জড়িত।

একটি সার্ভার সাইড পদ্ধতিগত ফাংশন দ্রুত এই চেয়ে কিছু এতদূর পোস্ট করা হয়েছে কারণ এটি শুধুমাত্র একটি প্রয়োজন একক অনুক্রমিক স্ক্যান টেবিল এবং একটি ওভার একক সাজানোর অপারেশন । যদি কোনও মানানসই সূচক পাওয়া যায় তবে কেবলমাত্র একটি একক সূচি-স্ক্যান।

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 এর জন্য, তিনটির জন্যপারফরম্যান্সের তুলনা করুন।জ্যাকের পরীক্ষার ক্ষেত্রেবিল্ডিং, সংশোধিত।

পোস্টগ্রিস 8.4 এর জন্য ডিবিফিডাল , যেখানে পারফরম্যান্সের পার্থক্য আরও বড়।


এটি কয়েকবার পড়ুন - এখনও আপনি তিনটি আর্গুমেন্টের count(x or null)পিছনে কী নিয়ে কথা বলছেন বা কখন আপনাকে এটি ব্যবহার করতে হবে বা এটি সেখানে কী করছে তা সম্পর্কে নিশ্চিত নয়। সম্ভবত আপনি কিছু নমুনা যেখানে এটি দেখাতে পারে হয় , প্রয়োজনীয় কারণ তা এখানে প্রয়োজনীয় না। এবং, এই কোণার কেসগুলি আবরণ করার জন্য কী কী প্রয়োজন হবে। বিটিডাব্লু, আমি আমার প্লেন / পিএসএসকিএল উদাহরণের জন্য আমার ডাউনটাতে আপটোটে পরিবর্তন করেছি। ওটা সত্যিই ভালো. (তবে, সাধারণত আমি সেই উত্তরের বিরোধী যা অন্যান্য উত্তরগুলির সংক্ষিপ্তসার বা কর্নার কেসগুলি কভার করে - যদিও আমি এটি বলার মতো ঘৃণা করি না যে এটি একটি কোণার মামলা কারণ এটি আমি বুঝতে পারি না)।
ইভান ক্যারল

আমি তাদের দুটি পৃথক স্ব-জবাবদিহি প্রশ্নাবলীর মধ্যে রাখব কারণ আমি নিশ্চিত যে আমিই একমাত্র ভাবছি না যে কী 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
;

ডিবি ফিডল এ এই কাজটি দেখুন (বা 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

এই বিষয়ে আরও পড়ার জন্য:

প্রথম লিঙ্কটি আপনাকে এমন কিছু কোড দেয় যা আপনি শুরু বা শেষের তারিখটি পূর্ববর্তী বা পরবর্তী সময়ের সমাপ্তি / আরম্ভের তারিখের সমান হতে চাইলে আপনি ব্যবহার করতে পারেন (যাতে কোনও ফাঁক নেই)। প্লাসের অন্যান্য সংস্করণ যা আপনাকে আপনার ক্যোয়ারিতে সহায়তা করতে পারে। যদিও তাদের এসকিউএল সার্ভার সিনট্যাক্স থেকে অনুবাদ করতে হবে ...


6

Postgres 8.4 এ আপনি একটি পুনরুত্থিত ফাংশন ব্যবহার করতে পারেন ।

তারা এটা কিভাবে করল

রিকার্সিভ ফাংশনটি প্রতিটি আলাদা আইডি_ টাইপে একটি স্তর যুক্ত করে, সাজানো ক্রমের উপর একের পর এক তারিখ নির্বাচন করে।

       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 এর গণিত কলাম প্রতিটি দ্বীপের শুরু চিহ্নিত করে। অতিরিক্তভাবে, নেস্টেড SELECT প্রতিটি সারির আগের তারিখ এবং ডেটাসেটের শেষ তারিখ প্রকাশ করে।

সারিগুলির জন্য যা তাদের নিজ দ্বীপের শুরু, আগের তারিখটি কার্যকরভাবে পূর্ববর্তী দ্বীপের শেষের তারিখ। মূল নির্বাচনটি এটি হিসাবে ব্যবহার করে। এটা শুধুমাত্র মিলে সারি পছন্দ is_start = 1শর্ত, এবং প্রতিটি ফিরে সারিতে সারি নিজস্ব দেখায় dateযেমন beginএবং নিম্নলিখিত সারি এর prev_dateহিসাবে end। যেহেতু শেষ সারিতে নিম্নলিখিত সারিটি নেই, LEAD(prev_date)এটির জন্য একটি শূন্য ফেরত দেয়, যার জন্য COALESCE ফাংশনটি ডেটাসেটের শেষ তারিখকে প্রতিস্থাপন করে।

আপনি এই সমাধানটি ডিবিফিডেলে খেলতে পারেন ।

দ্বীপগুলি চিহ্নিত করার জন্য অতিরিক্ত কলামগুলি প্রবর্তন করার সময়, আপনি সম্ভবত প্রতিটি উইন্ডো ফাংশনের ওভার ক্লজটিতে সাবকিলেস দ্বারা একটি পার্টিশন প্রবর্তন করতে চাইবেন। উদাহরণস্বরূপ, আপনি যদি একটি দ্বারা সংজ্ঞায়িত গ্রুপগুলির মধ্যে দ্বীপগুলি সনাক্ত করতে চান 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

ব্যবহারিক সমাধান হিসাবে শিক্ষাগত আগ্রহের চেয়ে বেশি, আপনি এটি ব্যবহারকারী-সংজ্ঞায়িত সমষ্টি দ্বারাও অর্জন করতে পারেন । অন্যান্য সমাধানগুলির মতো, এটি পোস্টগ্রিস ৮.৪ তেও কাজ করবে, তবে অন্যরা যেমন মন্তব্য করেছেন, দয়া করে আপগ্রেড করুন।

সমষ্টি হ্যান্ডলগুলি 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

এখানে ডিবিফিডল


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 লাগে, কোনও টেবিলের জন্য আকারের দ্বিগুণ, এটি 28 সেকেন্ড নেয়। আরও কয়েকটি নমুনা বাস্তবায়নের সময় ও (O n 2) এর মতো দেখায়।

ইভান ক্যারোলের পদ্ধতিটি 1 সেকেন্ডেরও কম সময় নেয় (যেমন: এটির জন্য যান!), এবং দেখতে ও (এন) এর মতো লাগে। রিকার্সিভ কোয়েরিগুলি একেবারে অক্ষম, এবং এটি একটি শেষ রিসোর্ট হিসাবে বিবেচনা করা উচিত।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.