PostgreSQL DISTINCT को अलग-अलग ORDER BY के साथ


216

मैं इस क्वेरी को चलाना चाहता हूं:

SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY purchases.purchased_at DESC

लेकिन मुझे यह त्रुटि मिली:

पीजी :: त्रुटि: त्रुटि: चयन सूची में अभिव्यक्तियों को प्रारंभिक आदेश द्वारा अभिव्यक्ति से मेल खाना चाहिए

address_idपहली ORDER BYअभिव्यक्ति के रूप में जोड़ना त्रुटि को शांत करता है, लेकिन मैं वास्तव में छँटाई को जोड़ना नहीं चाहता address_id। क्या बिना आदेश के ऐसा करना संभव है address_id?


आपके ऑर्डर क्लॉज ने_तो पता नहीं खरीदा है। आप अपना सवाल स्पष्ट कर सकते हैं।
तेजा

मेरे ऑर्डर ने खरीदारी की है क्योंकि मैं इसे चाहता हूं, लेकिन पोस्टग्रेज्स पते के लिए भी पूछता है (त्रुटि संदेश देखें)।
sl_bug

3
यहाँ पूरी तरह से उत्तर दिया गया है - stackoverflow.com/questions/9796078/… stackoverflow.com/users/268273/mosty-mostacho
sl_bug

व्यक्तिगत रूप से मुझे लगता है कि ORDER BY से मिलान करने के लिए DISTINCT ON की आवश्यकता बहुत ही संदेहास्पद है, क्योंकि उनके अलग-अलग होने के लिए कई तरह के वैध उपयोग के मामले हैं। Postgresql.uservoice पर एक पोस्ट है जो समान रूप से महसूस करने वालों के लिए इसे बदलने की कोशिश कर रहा है। postgresql.uservoice.com/forums/21853-general/suggestions/…
अर्धविराम

ठीक वैसा ही मुद्दा मिला, और उसी सीमा का सामना करना पड़ा। फिलहाल मैंने इसे एक उप-क्वेरी में तोड़ दिया है और फिर ऑर्डर दे रहा है, लेकिन यह गंदा लगता है।
गाइ पार्क

जवाबों:


208

प्रलेखन कहता है:

DISTINCT ON (अभिव्यक्ति [, ...]) उन पंक्तियों के प्रत्येक सेट की पहली पंक्ति रखता है जहाँ दिए गए भाव समान मूल्यांकन करते हैं। [...] ध्यान दें कि प्रत्येक सेट की "पहली पंक्ति" अप्रत्याशित है जब तक कि यह सुनिश्चित करने के लिए ORDER BY का उपयोग नहीं किया जाता है कि वांछित पंक्ति पहले दिखाई देती है। [...] DISTINCT ON अभिव्यक्ति (ओं) को बाईं ओर की अभिव्यक्ति से मेल खाना चाहिए।

आधिकारिक दस्तावेज

इसलिए आपको address_idऑर्डर को जोड़ना होगा ।

वैकल्पिक रूप से, यदि आप पूरी पंक्ति की तलाश कर रहे हैं, जिसमें प्रत्येक के लिए सबसे हाल ही में खरीदा गया उत्पाद है address_idऔर purchased_atतब तक के लिए हल किया गया है, तो आप समूह की सबसे बड़ी समस्या को हल करने का प्रयास कर रहे हैं जिसे निम्नलिखित तरीकों से हल किया जा सकता है:

सामान्य समाधान जो अधिकांश DBMS में काम करना चाहिए:

SELECT t1.* FROM purchases t1
JOIN (
    SELECT address_id, max(purchased_at) max_purchased_at
    FROM purchases
    WHERE product_id = 1
    GROUP BY address_id
) t2
ON t1.address_id = t2.address_id AND t1.purchased_at = t2.max_purchased_at
ORDER BY t1.purchased_at DESC

@ Hkf के उत्तर के आधार पर एक अधिक पोस्टग्रेसीक्यूएल-उन्मुख समाधान:

SELECT * FROM (
  SELECT DISTINCT ON (address_id) *
  FROM purchases 
  WHERE product_id = 1
  ORDER BY address_id, purchased_at DESC
) t
ORDER BY purchased_at DESC

समस्या यहाँ स्पष्ट, विस्तारित और हल की गई है: कुछ कॉलम द्वारा आदेशित पंक्तियों का चयन करना और दूसरे पर अलग


40
यह काम करता है, लेकिन गलत ऑर्डर देता है। इसलिए मैं आदेश खंड में address_id से छुटकारा पाना चाहता
हूं

1
दस्तावेज़ीकरण स्पष्ट है: आप इसलिए नहीं कर सकते क्योंकि चयनित पंक्ति अप्रत्याशित होगी
Mosty Mostacho

3
लेकिन दूर के पते के लिए नवीनतम खरीद का चयन करने का एक और तरीका हो सकता है?
sl_bug

1
यदि आपको खरीद के लिए ऑर्डर करने की आवश्यकता है। तो आप अपनी DISTINCT स्थितियों में खरीदे गए_ को जोड़ सकते हैं SELECT DISTINCT ON (purchases.purchased_at, address_id):। हालाँकि, एक ही address_id के साथ दो रिकॉर्ड किए गए, लेकिन अलग-अलग खरीदे गए मानों के परिणामस्वरूप दिए गए सेट में डुप्लिकेट होंगे। सुनिश्चित करें कि आप उस डेटा से परिचित हैं जिसे आप क्वेरी कर रहे हैं।
ब्रेंडन बेन्सन

23
प्रश्न की भावना स्पष्ट है। शब्दार्थ पर लेने की जरूरत नहीं। यह दुखद है कि स्वीकृत और सबसे अधिक मत दिया गया उत्तर आपको समस्या को हल करने में मदद नहीं करता है।
निकोगा

55

आप एक subquery में address_id द्वारा ऑर्डर कर सकते हैं, फिर बाहरी क्वेरी में आप जो चाहते हैं, उसके अनुसार ऑर्डर कर सकते हैं।

SELECT * FROM 
    (SELECT DISTINCT ON (address_id) purchases.address_id, purchases.* 
    FROM "purchases" 
    WHERE "purchases"."product_id" = 1 ORDER BY address_id DESC ) 
ORDER BY purchased_at DESC

3
लेकिन यह सिर्फ एक क्वेरी से धीमी होगी, नहीं?
sl_bug

2
बहुत मामूली रूप से हाँ। यद्यपि आपके पास एक खरीद है। * आपके मूल में select, मुझे नहीं लगता कि यह उत्पादन कोड है?
22 अक्टूबर को hkf

8
मैं जोड़ूंगा कि उपसंहार के नए संस्करणों के लिए आपको उप-वर्ग को बदलने की आवश्यकता है। उदाहरण के लिए: चुनें * से (।। DISTINCT पर (address_id) purchases.address_id, खरीदारी का चयन करें "खरीद" कहाँ "खरीद" से * "product_id" address_id DESC द्वारा = 1 आदेश) के रूप में tmp आदेश द्वारा DESC tmp.purchased_at
aembke

यह address_idदो बार (आवश्यकता के बिना) वापस आ जाएगा । कई ग्राहकों को डुप्लिकेट कॉलम नामों के साथ समस्या है। ORDER BY address_id DESCव्यर्थ और भ्रामक है। यह इस क्वेरी में कुछ भी उपयोगी नहीं है। परिणाम एक ही के साथ पंक्तियों के प्रत्येक सेट से एक मनमाना पिक है address_id, नवीनतम के साथ पंक्ति नहीं purchased_at। अस्पष्ट प्रश्न ने यह स्पष्ट रूप से नहीं पूछा, लेकिन यह लगभग निश्चित रूप से ओपी का इरादा है। संक्षेप में: इस क्वेरी का उपयोग न करें । मैंने स्पष्टीकरण के साथ विकल्प पोस्ट किए।
इरविन ब्रान्डसेट्टर

मेरे लिए काम किया। बहुत बढ़िया जवाब।
मैट वेस्ट

46

एक उपश्रेणी इसे हल कर सकती है:

SELECT *
FROM  (
    SELECT DISTINCT ON (address_id) *
    FROM   purchases
    WHERE  product_id = 1
    ) p
ORDER  BY purchased_at DESC;

ORDER BYस्तंभों से सहमत होने के लिए अभिव्यक्तियों में अग्रणी है DISTINCT ON, इसलिए आप एक ही समय में विभिन्न स्तंभों द्वारा आदेश नहीं दे सकते SELECT

ORDER BYयदि आप प्रत्येक सेट से एक विशेष पंक्ति चुनना चाहते हैं, तो केवल उप-वर्ग में एक अतिरिक्त का उपयोग करें :

SELECT *
FROM  (
    SELECT DISTINCT ON (address_id) *
    FROM   purchases
    WHERE  product_id = 1
    ORDER  BY address_id, purchased_at DESC  -- get "latest" row per address_id
    ) p
ORDER  BY purchased_at DESC;

अगर purchased_atहो सकता है NULL, विचार करें DESC NULLS LAST। यदि आप इसका उपयोग करने का इरादा रखते हैं, तो अपने सूचकांक का मिलान सुनिश्चित करें। देख:

अधिक विवरण के साथ, संबंधित:


आप DISTINCT ONएक मिलान के बिना उपयोग नहीं कर सकते ORDER BY। पहली क्वेरी को ORDER BY address_idउपकुंजी के अंदर की आवश्यकता होती है ।
अरस्तू पगलतज़िस

4
@AistotlePagaltzis: लेकिन आप कर सकते हैं । जहां से भी आपको यह मिला है, वह गलत है। आप उसी क्वेरी के DISTINCT ONबिना उपयोग कर सकते हैं ORDER BY। आपको DISTINCT ONइस मामले में खंड द्वारा परिभाषित साथियों के प्रत्येक सेट से एक मनमानी पंक्ति मिलती है । इसे आज़माएं या विवरण और मैनुअल के लिंक के लिए ऊपर दिए गए लिंक का पालन करें। ORDER BYएक ही क्वेरी में (एक ही SELECT) सिर्फ असहमत नहीं हो सकते DISTINCT ON। मैंने उसे भी समझाया।
एरविन ब्रान्डस्टेट्टर

हुह, तुम सही हो। ORDER BYडॉक्स में "अप्रत्याशित जब तक उपयोग नहीं किया जाता है" नोट के निहितार्थ के लिए मैं अंधा था क्योंकि यह मेरे लिए समझ में नहीं आता है कि यह सुविधा मूल्यों के गैर-निरंतर सेट के साथ सक्षम होने के लिए लागू की गई है ... फिर भी आपको अनुमति नहीं देगा एक स्पष्ट आदेश के साथ शोषण। कष्टप्रद।
अरस्तू पगलतज़िस

@AistotlePagaltzis: ऐसा इसलिए है, क्योंकि आंतरिक रूप से, Postgres एक (कम से कम) दो अलग-अलग एल्गोरिदम का उपयोग करता है: या तो किसी क्रमबद्ध सूची को पार करता है या हैश मानों के साथ काम करता है - जो भी तेज़ होने का वादा करता है। बाद के मामले में परिणाम DISTINCT ONअभिव्यक्ति (अभी तक) द्वारा सॉर्ट नहीं किया गया है ।
एरविन ब्रैंडसेटेटर

2
धन्यवाद। आपके उत्तर हमेशा क्रिस्टल स्पष्ट और सहायक होते हैं!
एंड्री डाइनको

10

विंडो फ़ंक्शन एक पास में हल कर सकता है:

SELECT DISTINCT ON (address_id) 
   LAST_VALUE(purchases.address_id) OVER wnd AS address_id
FROM "purchases"
WHERE "purchases"."product_id" = 1
WINDOW wnd AS (
   PARTITION BY address_id ORDER BY purchases.purchased_at DESC
   ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)

7
यह अच्छा होगा यदि किसी ने क्वेरी को समझाया।
गजस

@ गजस: संक्षिप्त विवरण: यह काम नहीं करता है, केवल अलग रिटर्न देता है address_id। सिद्धांत काम कर सकता है , हालांकि। संबंधित उदाहरण: stackoverflow.com/a/22064571/939860 या stackoverflow.com/a/11533808/939860 । लेकिन हाथ में समस्या के लिए छोटे और / या तेज प्रश्न हैं।
इरविन ब्रान्डस्टेट्टर

5

फ्लास्क-SQLAlchemy का उपयोग करने वाले किसी के लिए, यह मेरे लिए काम करता है

from app import db
from app.models import Purchases
from sqlalchemy.orm import aliased
from sqlalchemy import desc

stmt = Purchases.query.distinct(Purchases.address_id).subquery('purchases')
alias = aliased(Purchases, stmt)
distinct = db.session.query(alias)
distinct.order_by(desc(alias.purchased_at))

2
हां, या इससे भी आसान, मैं उपयोग करने में सक्षम था:query.distinct(foo).from_self().order(bar)
लॉरेंट मेयर

@LaurentMeyer क्या आपका मतलब है Purchases.query?
रबानो

हां, मेरा मतलब है क्रेचेस.क्वारी
लॉरेंट मेयर

-2

आप इसे क्लॉज द्वारा समूह का उपयोग करके भी कर सकते हैं

   SELECT purchases.address_id, purchases.* FROM "purchases"
    WHERE "purchases"."product_id" = 1 GROUP BY address_id,
purchases.purchased_at ORDER purchases.purchased_at DESC

यह गलत है (जब तक purchasesकि केवल दो कॉलम न हों address_idऔर purchased_at)। की वजह से GROUP BY, आप, समूहीकरण के लिए इस्तेमाल नहीं प्रत्येक स्तंभ के मूल्य प्राप्त करने के एक समग्र समारोह का उपयोग करने की आवश्यकता होगी ताकि उन सभी को समूह के विभिन्न पंक्तियों से आ रही हो जाएगा मूल्यों जब तक आप बदसूरत और अक्षम जिमनास्टिक के माध्यम से जाना। इसे केवल विंडो फ़ंक्शंस का उपयोग करके ठीक किया जा सकता है GROUP BY
अरस्तू पगलतज़िस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.