PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

विंडो फ़ंक्शंस या सामान्य तालिका अभिव्यक्तियाँ:पिछली पंक्तियों को सीमा के भीतर गिनें

मुझे नहीं लगता कि आप इसे सादे क्वेरी, सीटीई और विंडो फ़ंक्शंस के साथ सस्ते में कर सकते हैं - उनकी फ्रेम परिभाषा स्थिर है, लेकिन आपको एक डायनेमिक फ्रेम की आवश्यकता है (स्तंभ मानों के आधार पर)।

आम तौर पर, आपको अपनी विंडो की निचली और ऊपरी सीमा को सावधानीपूर्वक परिभाषित करना होगा:निम्नलिखित क्वेरी बहिष्कृत वर्तमान पंक्ति और शामिल करें निचली सीमा।
अभी भी एक मामूली अंतर है:फ़ंक्शन में वर्तमान पंक्ति के पिछले समकक्ष शामिल हैं, जबकि सहसंबद्ध उपश्रेणी उन्हें बाहर करती है ...

टेस्ट केस

ts . का उपयोग करना आरक्षित शब्द के बजाय date स्तंभ नाम के रूप में।

CREATE TABLE test (
  id  bigint
, ts  timestamp
);

ROM - रोमन की क्वेरी

CTE का उपयोग करें, टाइमस्टैम्प को एक सरणी में एकत्रित करें, अननेस्ट करें, गिनें ...
सही होने पर, प्रदर्शन बेहद खराब हो जाता है पंक्तियों से भरे एक हाथ से अधिक के साथ। यहां कुछ प्रदर्शन हत्यारे हैं। नीचे देखें।

ARR - सरणी तत्वों की गणना करें

मैंने रोमन की क्वेरी ली और इसे थोड़ा सरल बनाने की कोशिश की:

  • दूसरा सीटीई हटाएं जो जरूरी नहीं है।
  • पहले सीटीई को सबक्वेरी में रूपांतरित करें, जो तेज है।
  • प्रत्यक्ष count() एक सरणी में फिर से एकत्र होने और array_length() . के साथ गिनती करने के बजाय ।

लेकिन सरणी प्रबंधन महंगा है, और प्रदर्शन अभी भी बुरी तरह से खराब होता है अधिक पंक्तियों के साथ।

SELECT id, ts
     , (SELECT count(*)::int - 1
        FROM   unnest(dates) x
        WHERE  x >= sub.ts - interval '1h') AS ct
FROM (
   SELECT id, ts
        , array_agg(ts) OVER(ORDER BY ts) AS dates
   FROM   test
   ) sub;

COR - सहसंबद्ध सबक्वेरी

आप कर सकते थे इसे एक साधारण सहसंबद्ध उपश्रेणी के साथ हल करें। बहुत तेज़, लेकिन फिर भी...

SELECT id, ts
     , (SELECT count(*)
        FROM   test t1
        WHERE  t1.ts >= t.ts - interval '1h'
        AND    t1.ts < t.ts) AS ct
FROM   test t
ORDER  BY ts;

FNC - फ़ंक्शन

एक row_number() . के साथ कालानुक्रमिक क्रम में पंक्तियों पर लूप करें plpgsql फ़ंक्शन . में और उसे एक कर्सर . के साथ संयोजित करें वांछित समय सीमा में फैले एक ही प्रश्न पर। तब हम केवल पंक्ति संख्या घटा सकते हैं:

CREATE OR REPLACE FUNCTION running_window_ct(_intv interval = '1 hour')
  RETURNS TABLE (id bigint, ts timestamp, ct int)
  LANGUAGE plpgsql AS
$func$
DECLARE
   cur   CURSOR FOR
         SELECT t.ts + _intv AS ts1, row_number() OVER (ORDER BY t.ts) AS rn
         FROM   test t ORDER BY t.ts;
   rec   record;
   rn    int;

BEGIN
   OPEN cur;
   FETCH cur INTO rec;
   ct := -1;  -- init

   FOR id, ts, rn IN
      SELECT t.id, t.ts, row_number() OVER (ORDER BY t.ts)
      FROM   test t ORDER BY t.ts
   LOOP
      IF rec.ts1 >= ts THEN
         ct := ct + 1;
      ELSE
         LOOP
            FETCH cur INTO rec;
            EXIT WHEN rec.ts1 >= ts;
         END LOOP;
         ct := rn - rec.rn;
      END IF;

      RETURN NEXT;
   END LOOP;
END
$func$;

एक घंटे के डिफ़ॉल्ट अंतराल के साथ कॉल करें:

SELECT * FROM running_window_ct();

या किसी भी अंतराल के साथ:

SELECT * FROM running_window_ct('2 hour - 3 second');

db<>फिडल यहाँ
पुराना sqlfiddle

बेंचमार्क

ऊपर से तालिका के साथ मैंने अपने पुराने परीक्षण सर्वर पर एक त्वरित बेंचमार्क चलाया:(डेबियन पर पोस्टग्रेएसक्यूएल 9.1.9)।

-- TRUNCATE test;
INSERT INTO test
SELECT g, '2013-08-08'::timestamp
         + g * interval '5 min'
         + random() * 300 * interval '1 min' -- halfway realistic values
FROM   generate_series(1, 10000) g;

CREATE INDEX test_ts_idx ON test (ts);
ANALYZE test;  -- temp table needs manual analyze

मैंने बोल्ड . में बदलाव किया प्रत्येक रन के लिए भाग लिया और EXPLAIN ANALYZE . के साथ सर्वश्रेष्ठ 5 लिया ।

100 पंक्तियां
ROM:27.656 ms
ARR:7.834 ms
COR:5.488 ms
FNC:1.115 ms

1000 पंक्तियां
ROM:2116.029 ms
ARR:189.679 ms
COR:65.802 ms
FNC:8.466 ms

5000 पंक्तियां
ROM:51347 ms !!
ARR:3167 ms
COR:333 ms
FNC:42 ms

100000 पंक्तियां
ROM:DNF
ARR:DNF
COR:6760 ms
FNC:828 ms

समारोह स्पष्ट विजेता है। यह परिमाण और पैमानों के क्रम से सबसे तेज़ है।
सरणी प्रबंधन प्रतिस्पर्धा नहीं कर सकता।



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL गणित कार्य

  2. PostgreSQL में बल्क/बैच अपडेट/अपर्ट

  3. पोस्टग्रेज में कॉलम को कई पंक्तियों में विभाजित करें

  4. आप पोस्टग्रेज / पोस्टग्रेएसक्यूएल टेबल और उसके इंडेक्स के डिस्क आकार को कैसे ढूंढते हैं?

  5. अपने PostgreSQL इंडेक्स का अधिकतम लाभ उठाएं