सबसे पहले चीज़ें:आप कर सकते हैं एक ही क्वेरी में एक सीटीई से कई बार परिणामों का उपयोग करें, यह एक मुख्य की विशेषता है सीटीई ।) आपके पास जो होगा वह इस तरह काम करेगा (जबकि अभी भी केवल एक बार सीटीई का उपयोग करते हुए):
WITH cte AS (
SELECT * FROM (
SELECT *, row_number() -- see below
OVER (PARTITION BY person_id
ORDER BY submission_date DESC NULLS LAST -- see below
, last_updated DESC NULLS LAST -- see below
, id DESC) AS rn
FROM tbl
) sub
WHERE rn = 1
AND status IN ('ACCEPTED', 'CORRECTED')
)
SELECT *, count(*) OVER () AS total_rows_in_cte
FROM cte
LIMIT 10
OFFSET 0; -- see below
चेतावनी 1:rank()
rank()
प्रति person_id
के लिए कई पंक्तियाँ लौटा सकते हैं rank = 1
. के साथ . DISTINCT ON (person_id)
(जैसे गॉर्डन प्रदान किया गया) row_number()
. के लिए एक लागू प्रतिस्थापन है - जो आपके लिए काम करता है, जैसा कि अतिरिक्त जानकारी स्पष्ट किया गया है। देखें:
चेतावनी 2:ORDER BY submission_date DESC
न तो submission_date
न ही last_updated
परिभाषित हैं NOT NULL
. ORDER BY submission_date DESC, last_updated DESC ...
के साथ एक समस्या हो सकती है देखें:
क्या वे कॉलम वास्तव में NOT NULL
होने चाहिए? ?
आपने उत्तर दिया:
date
. प्रकार के लिए खाली स्ट्रिंग्स की अनुमति नहीं है . कॉलम को अशक्त रखें। NULL
उन मामलों के लिए उचित मूल्य है। NULLS LAST
Use का उपयोग करें जैसा कि NULL
. से बचने के लिए दिखाया गया है शीर्ष पर क्रमबद्ध किया जा रहा है।
चेतावनी 3:OFFSET
अगर OFFSET
सीटीई द्वारा लौटाई गई पंक्तियों की संख्या के बराबर या अधिक है, आपको कोई पंक्ति नहीं . मिलती है , इसलिए भी कोई कुल गिनती नहीं है। देखें:
अंतरिम समाधान
अब तक सभी चेतावनियों को संबोधित करते हुए, और अतिरिक्त जानकारी के आधार पर, हम इस प्रश्न पर पहुंच सकते हैं:
WITH cte AS (
SELECT DISTINCT ON (person_id) *
FROM tbl
WHERE status IN ('ACCEPTED', 'CORRECTED')
ORDER BY person_id, submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC
)
SELECT *
FROM (
TABLE cte
ORDER BY person_id -- ?? see below
LIMIT 10
OFFSET 0
) sub
RIGHT JOIN (SELECT count(*) FROM cte) c(total_rows_in_cte) ON true;
अब सीटीई असल में है दो बार इस्तेमाल किया। RIGHT JOIN
गारंटी है कि हम कुल गिनती प्राप्त करते हैं, कोई फर्क नहीं पड़ता OFFSET
. DISTINCT ON
प्रति (person_id)
. में केवल कुछ पंक्तियों के लिए OK-ish का प्रदर्शन करना चाहिए मूल क्वेरी में।
लेकिन आपके पास विस्तृत पंक्तियाँ हैं। औसतन कितना चौड़ा? क्वेरी के परिणामस्वरूप संपूर्ण तालिका पर क्रमिक स्कैन होने की संभावना है। इंडेक्स मदद नहीं करेंगे (ज्यादा)। यह सब पेजिंग के लिए बेहद अक्षम . रहेगा . देखें:
आप पेजिंग के लिए एक इंडेक्स शामिल नहीं कर सकते क्योंकि यह सीटीई से व्युत्पन्न तालिका पर आधारित है। और पेजिंग के लिए आपका वास्तविक सॉर्ट मानदंड अभी भी स्पष्ट नहीं है (ORDER BY id
?) यदि पेजिंग लक्ष्य है, तो आपको एक अलग क्वेरी शैली की सख्त आवश्यकता है। यदि आप केवल पहले कुछ पृष्ठों में रुचि रखते हैं, तो आपको एक अलग क्वेरी शैली की आवश्यकता है, फिर भी। सबसे अच्छा समाधान उस जानकारी पर निर्भर करता है जो अभी भी प्रश्न में गायब है ...
अत्यधिक तेज़
आपके अपडेट किए गए उद्देश्य के लिए:
("निर्दिष्ट फ़िल्टर मानदंड, प्रकार, योजना, स्थिति के लिए" को अनदेखा करना सादगी के लिए।)
और:
इन दो विशिष्ट सूचकांकों . के आधार पर :
CREATE INDEX ON tbl (submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST)
WHERE status IN ('ACCEPTED', 'CORRECTED'); -- optional
CREATE INDEX ON tbl (person_id, submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST);
यह क्वेरी चलाएँ:
WITH RECURSIVE cte AS (
(
SELECT t -- whole row
FROM tbl t
WHERE status IN ('ACCEPTED', 'CORRECTED')
AND NOT EXISTS (SELECT FROM tbl
WHERE person_id = t.person_id
AND ( submission_date, last_updated, id)
> (t.submission_date, t.last_updated, t.id) -- row-wise comparison
)
ORDER BY submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST
LIMIT 1
)
UNION ALL
SELECT (SELECT t1 -- whole row
FROM tbl t1
WHERE ( t1.submission_date, t1.last_updated, t1.id)
< ((t).submission_date,(t).last_updated,(t).id) -- row-wise comparison
AND t1.status IN ('ACCEPTED', 'CORRECTED')
AND NOT EXISTS (SELECT FROM tbl
WHERE person_id = t1.person_id
AND ( submission_date, last_updated, id)
> (t1.submission_date, t1.last_updated, t1.id) -- row-wise comparison
)
ORDER BY submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST
LIMIT 1)
FROM cte c
WHERE (t).id IS NOT NULL
)
SELECT (t).*
FROM cte
LIMIT 10
OFFSET 0;
यहां कोष्ठकों के प्रत्येक सेट की आवश्यकता है।
परिष्कार के इस स्तर को दिए गए सूचकांकों का उपयोग करके और कोई अनुक्रमिक स्कैन नहीं करके शीर्ष पंक्तियों के अपेक्षाकृत छोटे सेट को मौलिक रूप से तेज़ी से प्राप्त करना चाहिए। देखें:
submission_date
संभवतः टाइप timestamptz
. होना चाहिए या date
, नहीं <स्ट्राइक>character varying(255)
स्ट्राइक> - जो किसी भी मामले में Postgres में एक विषम प्रकार की परिभाषा है। देखें:
कई और विवरण अनुकूलित किए जा सकते हैं, लेकिन यह हाथ से निकल रहा है। आप पेशेवर परामर्श पर विचार कर सकते हैं।