PostgreSQL में समूहीकृत समूह: प्रत्येक समूह के लिए पहली N पंक्तियाँ दिखाएँ?


179

मुझे कस्टम कॉलम द्वारा आदेशित प्रत्येक समूह के लिए पहली एन पंक्तियाँ लेने की आवश्यकता है।

निम्नलिखित तालिका को देखते हुए:

db=# SELECT * FROM xxx;
 id | section_id | name
----+------------+------
  1 |          1 | A
  2 |          1 | B
  3 |          1 | C
  4 |          1 | D
  5 |          2 | E
  6 |          2 | F
  7 |          3 | G
  8 |          2 | H
(8 rows)

मुझे प्रत्येक अनुभाग के लिए पहले 2 पंक्तियों ( नाम द्वारा आदेशित ) की आवश्यकता है , अर्थात इसके समान परिणाम:

 id | section_id | name
----+------------+------
  1 |          1 | A
  2 |          1 | B
  5 |          2 | E
  6 |          2 | F
  7 |          3 | G
(5 rows)

मैं PostgreSQL 8.3.5 का उपयोग कर रहा हूं।

जवाबों:


279

नया समाधान (PostgreSQL 8.4)

SELECT
  * 
FROM (
  SELECT
    ROW_NUMBER() OVER (PARTITION BY section_id ORDER BY name) AS r,
    t.*
  FROM
    xxx t) x
WHERE
  x.r <= 2;

8
यह PostgreSQL 8.4 के साथ भी काम करता है (विंडो फ़ंक्शन 8.4 से शुरू होता है)।
ब्रूनो

2
पाठ्यपुस्तक का उत्तर समूहीकृत सीमा
पिगीबॉक्स

4
बहुत बढ़िया! यह निर्दोष रूप से काम करता है। मैं हालांकि उत्सुक हूं, क्या ऐसा करने का कोई तरीका है group by?
नूरशोमिक

1
उन लोगों के लिए जो लाखों पंक्तियों के साथ काम करते हैं और ऐसा करने के लिए वास्तव में अच्छा तरीका तलाशते हैं - पॉशेस्ट का उत्तर जाने का तरीका है। बस उचित अनुक्रमण के साथ ti मसाला करने के लिए मत भूलना।
परिश्रमी की प्रेसर

37

V9.3 के बाद से आप लेटरल जॉइन कर सकते हैं

select distinct t_outer.section_id, t_top.id, t_top.name from t t_outer
join lateral (
    select * from t t_inner
    where t_inner.section_id = t_outer.section_id
    order by t_inner.name
    limit 2
) t_top on true
order by t_outer.section_id;

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


4
बहुत गूढ़ समाधान आईएमओ, विशेष रूप से उन नामों के साथ, लेकिन एक अच्छा।
विलास vill ’

1
LATERAL JOIN के साथ यह समाधान एक से अधिक विंडो किए गए फ़ंक्शन (कुछ मामलों में) के साथ एक से अधिक तेज़ी से हो सकता है यदि आपके पास t_inner.nameस्तंभ द्वारा अनुक्रमणिका है
Artur Rashitov

क्वेरी को समझना आसान है कि क्या इसमें स्व-शामिल नहीं है। उस मामले distinctमें जरूरत नहीं है। पोस्ट किए गए लिंक पॉशस्ट में एक उदाहरण दिखाया गया है।
gillesB

यार, यह मनगढंत बात है। 9sec के बजाय "ROW_NUMBER" समाधान के साथ 120ms। धन्यवाद!
मेहनती प्रमुख प्रेसर

हम t_top के सभी कॉलम का चयन कैसे कर सकते हैं। टी टेबल में एक जोंस कॉलम होता है और मुझे "टाइप" distinct t_outer.section_id, t_top.*
जौन

12

यहाँ एक और समाधान है (PostgreSQL <= 8.3)।

SELECT
  *
FROM
  xxx a
WHERE (
  SELECT
    COUNT(*)
  FROM
    xxx
  WHERE
    section_id = a.section_id
  AND
    name <= a.name
) <= 2

2
SELECT  x.*
FROM    (
        SELECT  section_id,
                COALESCE
                (
                (
                SELECT  xi
                FROM    xxx xi
                WHERE   xi.section_id = xo.section_id
                ORDER BY
                        name, id
                OFFSET 1 LIMIT 1
                ),
                (
                SELECT  xi
                FROM    xxx xi
                WHERE   xi.section_id = xo.section_id
                ORDER BY 
                        name DESC, id DESC
                LIMIT 1
                )
                ) AS mlast
        FROM    (
                SELECT  DISTINCT section_id
                FROM    xxx
                ) xo
        ) xoo
JOIN    xxx x
ON      x.section_id = xoo.section_id
        AND (x.name, x.id) <= ((mlast).name, (mlast).id)

क्वेरी को मेरी ज़रूरत के बहुत करीब है, सिवाय इसके कि यह 2 से कम पंक्तियों के साथ अनुभाग नहीं दिखा रहा है, अर्थात आईडी = 7 वाली पंक्ति वापस आ गई है। वरना मुझे आपका तरीका पसंद है।
कोउर सपारेव

धन्यवाद, मैं सिर्फ COALESCE के साथ एक ही समाधान के लिए आया था, लेकिन आप तेज थे। :-)
कोउर सपारेव

वास्तव में अंतिम JOIN उप-खंड को सरल बनाया जा सकता है: ... और x.id <= (mlast)। आईडी नाम क्षेत्र के अनुसार पहले से ही चुना गया है, नहीं?
कोउर सपारेव

@Kouber: आपके उदाहरण में name's' और id's' समान क्रम में क्रमबद्ध हैं, इसलिए आप इसे नहीं देखेंगे। नामों को रिवर्स ऑर्डर में बनाएं और आप देखेंगे कि ये क्वेरी अलग-अलग परिणाम देती हैं।
क्वासोइ

2
        -- ranking without WINDOW functions
-- EXPLAIN ANALYZE
WITH rnk AS (
        SELECT x1.id
        , COUNT(x2.id) AS rnk
        FROM xxx x1
        LEFT JOIN xxx x2 ON x1.section_id = x2.section_id AND x2.name <= x1.name
        GROUP BY x1.id
        )
SELECT this.*
FROM xxx this
JOIN rnk ON rnk.id = this.id
WHERE rnk.rnk <=2
ORDER BY this.section_id, rnk.rnk
        ;

        -- The same without using a CTE
-- EXPLAIN ANALYZE
SELECT this.*
FROM xxx this
JOIN ( SELECT x1.id
        , COUNT(x2.id) AS rnk
        FROM xxx x1
        LEFT JOIN xxx x2 ON x1.section_id = x2.section_id AND x2.name <= x1.name
        GROUP BY x1.id
        ) rnk
ON rnk.id = this.id
WHERE rnk.rnk <=2
ORDER BY this.section_id, rnk.rnk
        ;

CTE और विंडो फ़ंक्शंस को एक ही संस्करण के साथ पेश किया गया था, इसलिए मुझे पहले समाधान का लाभ दिखाई नहीं देता है।
a_horse_with_no_name

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

पोस्ट को "postgresql" और पोस्टग्रेएसक्यूएल संस्करण को टैग किया गया है जिसने सीटीई की शुरुआत की, जिसमें विंडोिंग फ़ंक्शन भी प्रस्तुत किए गए। इसलिए मेरी टिप्पणी (मैंने देखा कि यह पुराना है - और PG 8.3 में न तो था)
a_horse_with_no_name

पोस्ट में 8.3.5 का उल्लेख है, और मुझे विश्वास है कि उन्हें 8.4 में पेश किया गया था। इसके अलावा: वैकल्पिक परिदृश्यों, IMHO के बारे में जानना भी अच्छा है।
Wildplasser 21

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