सबसे लंबे समय तक निरंतर अनुक्रम का चयन करें


12

मैं PostgreSQL 9.0 में एक क्वेरी बनाने की कोशिश कर रहा हूं जो एक विशिष्ट कॉलम के लिए निरंतर पंक्तियों का सबसे लंबा अनुक्रम प्राप्त करता है।

निम्नलिखित तालिका पर विचार करें:

lap_id (serial), lap_no (int), car_type (enum), race_id (int FK)

जहां lap_noप्रत्येक के लिए अद्वितीय है (race_id, car_type)

मैं किसी दिए गए race_idऔर सबसे लंबे अनुक्रम का निर्माण करने के लिए क्वेरी करना चाहूंगा car_type, इसलिए यह एक int(या लंबा) लौटाएगा जो उच्चतम है।

निम्नलिखित डेटा के साथ:

1, 1, red, 1
2, 2, red, 1
3, 3, red, 1
4, 4, red, 1
5, 1, blue, 1
6, 5, red, 1
7, 2, blue, 1
8, 1, green, 1

के लिए car_type = red and race_id = 1क्वेरी वापसी होगी 5की सबसे लंबी अनुक्रम के रूप में lap_noक्षेत्र।

मुझे यहां एक समान प्रश्न मिला, हालांकि मेरी स्थिति थोड़ी अधिक सीधी है।

(मैं car_typeसभी दौड़ के लिए दिए गए सबसे लंबे अनुक्रम को भी जानना चाहूंगा , लेकिन खुद को काम करने की योजना बना रहा था।)

जवाबों:


20

आपका विवरण इस तरह एक तालिका परिभाषा में परिणाम करता है :

CREATE TABLE tbl (
   lap_id   serial PRIMARY KEY
 , lap_no   int NOT NULL
 , car_type enum NOT NULL
 , race_id  int NOT NULL  -- REFERENCES ...
 , UNIQUE(race_id, car_type, lap_no)
);

समस्याओं के इस वर्ग के लिए सामान्य समाधान

सबसे लंबा अनुक्रम प्राप्त करने के लिए (1 परिणाम, सबसे लंबे समय तक, मनमाना उठाएं यदि संबंध हैं):

SELECT race_id, car_type, count(*) AS seq_len
FROM  (
   SELECT *, count(*) FILTER (WHERE step)
                      OVER (ORDER BY race_id, car_type, lap_no) AS grp
   FROM  (
      SELECT *, (lag(lap_no) OVER (PARTITION BY race_id, car_type ORDER BY lap_no) + 1)
                 IS DISTINCT FROM lap_no AS step
      FROM   tbl
      ) x
   ) y
GROUP  BY race_id, car_type, grp
ORDER  BY seq_len DESC
LIMIT  1;

count(*) FILTER (WHERE step)केवल मायने रखता है TRUE(= अगले समूह के लिए कदम), जिसके परिणामस्वरूप हर नए समूह के लिए एक नई संख्या होती है।

एसओ पर संबंधित प्रश्न, एक जवाब जो plpgsql के साथ एक प्रक्रियात्मक समाधान की विशेषता है :

यदि शीर्ष आवश्यकता प्रदर्शन की है, तो plpgsql फ़ंक्शन आम तौर पर इस विशेष मामले में तेज होता है क्योंकि यह एकल स्कैन में परिणाम की गणना कर सकता है।

लगातार संख्याओं के लिए तेज़

हम इस तथ्य को भुनाने के लिए कर सकते हैं कि लगातार lap_no एक अनुक्रम को परिभाषित करें, बहुत सरल और तेज संस्करण के लिए :

SELECT race_id, car_type, count(*) AS seq_len
FROM  (
   SELECT race_id, car_type
        , row_number() OVER (PARTITION BY race_id, car_type ORDER BY lap_no) - lap_no AS grp
   FROM   tbl
   ) x
GROUP  BY race_id, car_type, grp
ORDER  BY seq_len DESC
LIMIT  1;

लगातार अंतराल एक ही में समाप्त होता है grp। प्रत्येक लापता गोद का परिणाम grpप्रति विभाजन कम होता है।

यह (race_id, car_type, lap_no)होने पर निर्भर करता है UNIQUE NOT NULL। पूर्ण मान या डुप्लिकेट तर्क तोड़ सकता है।

जैक के सरल विकल्प की चर्चा

@ जैक का संस्करण प्रभावी रूप से सभी अंतराल (पंक्तियों) को गिनाता है जहां पिछले lap_noमें यह race_idसमान था car_type। यह सरल और तेज और सही है - जब तक कि प्रत्येक में car_typeकेवल एक अनुक्रम हो सकता है race_id

लेकिन एक कार्य है कि सरल क्वेरी के लिए सरल हो सकता है, अभी तक। यह तार्किक का पालन करेगा कि सभी lap_noप्रति (car_type, race_id)होना चाहिए क्रम में , और हम सिर्फ लैप गिनती कर सकते हैं:

SELECT race_id, car_type, count(*) AS seq_len
FROM   tbl
GROUP  BY race_id, car_type
ORDER  BY seq_len DESC
LIMIT  1;

अगर, दूसरी ओर, प्रति रेस_ड में कई अलग-अलग अनुक्रमcar_type हो सकते हैं (और प्रश्न अन्यथा निर्दिष्ट नहीं करता है), तो जैक का संस्करण विफल हो जाएगा।

किसी दिए गए दौड़ / कार के प्रकार के लिए तेज़

प्रश्न में टिप्पणी / स्पष्टीकरण के जवाब में: किसी दिए गए प्रश्न को सीमित करने से(race_id, car_type) यह बहुत तेज हो जाएगा , निश्चित रूप से:

SELECT count(*) AS seq_len
FROM  (
   SELECT row_number() OVER (ORDER BY lap_no) - lap_no AS grp
   FROM   tbl
   WHERE  race_id = 1
   AND    car_type = 'red'
   ) x
GROUP  BY grp
ORDER  BY seq_len DESC
LIMIT  1;

db <> यहाँ field
पुरानी SQL फ़ेल्ड

सूची

शीर्ष प्रदर्शन की कुंजी एक फिटिंग इंडेक्स है (एक एकल अनुक्रमिक स्कैन के साथ काम करने वाले उल्लिखित प्रक्रियात्मक समाधान को छोड़कर)। इस तरह का एक बहुरंगी सूचकांक सबसे अच्छा कार्य करता है:

CREATE INDEX tbl_mult_idx ON tbl (race_id, car_type, lap_no);

यदि आपकी तालिका में ऊपर की ओर UNIQUEलगाई गई बाधा है, तो इसे केवल इस (अद्वितीय) सूचकांक के साथ आंतरिक रूप से लागू किया गया है, और आपको दूसरा सूचकांक बनाने की आवश्यकता नहीं है।


हाय इरविन, धन्यवाद जो काम करता है, लेकिन यह मेरे डेटाबेस पर ~ 17sec लेता है! मान लीजिए कि आप एक संशोधन प्रदान कर सकते हैं तो यह पूरी तालिका की तुलना करने के बजाय रेस_ड और कार_टाइप को मापदंडों के रूप में लेता है? (मैंने इसे फिर से लिखने की कोशिश की है और त्रुटियों में भाग रहा
हूं

7

create table tbl (lap_no int, car_type text, race_id int);
insert into tbl values (1,'red',1),(2,'red',1),(3,'red',1),(4,'red',1),
                       (1,'blue',1),(5,'red',1),(2,'blue',1),(1,'green',1);
select car_type, race_id, sum(case when lap_no=(prev+1) then 1 else 0 end)+1 seq_len
from ( select *, lag(lap_no) over (partition by car_type, race_id order by lap_no) prev 
       from tbl ) z
group by car_type, race_id
order by seq_len desc limit 1;
/*
|car_type|race_id|seq_len|
|:-------|------:|------:|
|red     |      1|      5|
*/

या शायद sum((lap_no=(prev+1))::integer)+1लेकिन मुझे यकीन नहीं है कि यह पढ़ना आसान है
जैक कहते हैं कि 20ans में topanswers.xyz
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.