2009-07-14 14 views
115

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

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

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) 

मैं, प्रत्येक SECTION_ID के लिए पहले 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 का उपयोग कर।

उत्तर

186

नए समाधान (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

यह पोस्टग्रेएसक्यूएल 8.4 के साथ भी काम करता है (विंडो फ़ंक्शंस 8.4 के साथ शुरू होता है)। – Bruno

+2

अपने उदाहरण का उपयोग करने के लिए बहुत अच्छा :) –

+0

यह निश्चित रूप से सही उत्तर है - मन * उड़ा * – dineth

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) 
+0

मुझे मिल रहा है: त्रुटि: "जॉइन" –

+0

@ कोबर पर वाक्यविन्यास त्रुटि: पोस्ट अपडेट देखें – Quassnoi

+0

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

11

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

SELECT 
    * 
FROM 
    xxx a 
WHERE (
    SELECT 
    COUNT(*) 
    FROM 
    xxx 
    WHERE 
    section_id = a.section_id 
    AND 
    name <= a.name 
) <= 2 
1
 -- 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 
     ; 
+0

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

+0

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

+0

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

10

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; 

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

+0

मैं वास्तव में काम नहीं करता हूं। मैं वही आउटपुट प्राप्त करता हूं जब मैं पार्श्व में शामिल भाग को छोड़कर आपके बाकी की क्वेरी निष्पादित करता हूं, यानी आपकी क्वेरी की केवल पहली और आखिरी पंक्तियां लेना और उन्हें निष्पादित करना। इन दो निम्नलिखित मुझे वापस जाने के लिए एक ही परिणाम सेट: 'अलग a.customer_name का चयन करें, बिक्री से a.order_date एक में शामिल होने के पार्श्व ( \t चयन * बिक्री से \t जहां b.customer_name = a.customer_name \t आदेश ख ORDER_DATE desc \t सीमा 3 ) a.customer_name से सच आदेश पर सी द्वारा; ' और ' अलग customer_name का चयन करें, customer_name द्वारा बिक्री आदेश से ORDER_DATE; ' – NurShomik

+0

ओह, मेरी गलती। यह काम करता है, मुझे a.order_date के बजाय c.order_date का उपयोग करना पड़ा। – NurShomik

+0

बहुत ही गुप्त समाधान आईएमओ, विशेष रूप से उन नामों के साथ, लेकिन एक अच्छा है। – VillasV

संबंधित मुद्दे