PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

प्रति उपयोगकर्ता नवीनतम पंक्ति पुनर्प्राप्त करने के लिए क्वेरी द्वारा समूह अनुकूलित करें

सर्वोत्तम पठन प्रदर्शन के लिए आपको एक बहु-स्तंभ अनुक्रमणिका की आवश्यकता है:

CREATE INDEX log_combo_idx
ON log (user_id, log_date DESC NULLS LAST);

केवल अनुक्रमणिका स्कैन करने के लिए संभव है, अन्यथा आवश्यक नहीं कॉलम जोड़ें payload एक कवरिंग इंडेक्स में INCLUDE . के साथ क्लॉज (11 या बाद के संस्करण पोस्ट करें):

CREATE INDEX log_combo_covering_idx
ON log (user_id, log_date DESC NULLS LAST) INCLUDE (payload);

देखें:

  • क्या PostgreSQL में इंडेक्स को कवर करने से जॉइन कॉलम में मदद मिलती है?

पुराने संस्करणों के लिए फ़ॉलबैक:

CREATE INDEX log_combo_covering_idx
ON log (user_id, log_date DESC NULLS LAST, payload);

क्यों DESC NULLS LAST ?

  • दिनांक क्वेरी की श्रेणी में अप्रयुक्त अनुक्रमणिका

कुछ . के लिए पंक्तियाँ प्रति user_id या छोटी टेबल DISTINCT ON आमतौर पर सबसे तेज़ और सरल होता है:

  • समूह द्वारा प्रत्येक समूह में पहली पंक्ति का चयन करें?

कई . के लिए पंक्तियाँ प्रति user_id एक इंडेक्स स्किप स्कैन (या ढीला इंडेक्स स्कैन ) (बहुत) अधिक कुशल है। पोस्टग्रेज 12 तक इसे लागू नहीं किया गया है - पोस्टग्रेज 14 के लिए काम जारी है। लेकिन इसे कुशलता से अनुकरण करने के तरीके हैं।

सामान्य तालिका अभिव्यक्तियों के लिए पोस्टग्रेज की आवश्यकता होती है 8.4+ .
LATERAL पोस्टग्रेज की आवश्यकता है 9.3+ .
निम्न समाधान Postgres Wiki में शामिल किए गए समाधान से आगे जाते हैं ।

<एच3>1. अद्वितीय उपयोगकर्ताओं के साथ कोई अलग तालिका नहीं

एक अलग users के साथ तालिका, समाधान 2. . में नीचे आमतौर पर सरल और तेज़ होते हैं। आगे बढ़ें।

1a. LATERAL के साथ पुनरावर्ती सीटीई शामिल हों

WITH RECURSIVE cte AS (
   (                                -- parentheses required
   SELECT user_id, log_date, payload
   FROM   log
   WHERE  log_date <= :mydate
   ORDER  BY user_id, log_date DESC NULLS LAST
   LIMIT  1
   )
   UNION ALL
   SELECT l.*
   FROM   cte c
   CROSS  JOIN LATERAL (
      SELECT l.user_id, l.log_date, l.payload
      FROM   log l
      WHERE  l.user_id > c.user_id  -- lateral reference
      AND    log_date <= :mydate    -- repeat condition
      ORDER  BY l.user_id, l.log_date DESC NULLS LAST
      LIMIT  1
      ) l
   )
TABLE  cte
ORDER  BY user_id;

मनमाना कॉलम पुनर्प्राप्त करना आसान है और शायद वर्तमान पोस्टग्रेस में सबसे अच्छा है। अध्याय 2a. . में अधिक स्पष्टीकरण नीचे।

<एच4>1बी. सहसंबद्ध सबक्वेरी के साथ पुनरावर्ती सीटीई
WITH RECURSIVE cte AS (
   (                                           -- parentheses required
   SELECT l AS my_row                          -- whole row
   FROM   log l
   WHERE  log_date <= :mydate
   ORDER  BY user_id, log_date DESC NULLS LAST
   LIMIT  1
   )
   UNION ALL
   SELECT (SELECT l                            -- whole row
           FROM   log l
           WHERE  l.user_id > (c.my_row).user_id
           AND    l.log_date <= :mydate        -- repeat condition
           ORDER  BY l.user_id, l.log_date DESC NULLS LAST
           LIMIT  1)
   FROM   cte c
   WHERE  (c.my_row).user_id IS NOT NULL       -- note parentheses
   )
SELECT (my_row).*                              -- decompose row
FROM   cte
WHERE  (my_row).user_id IS NOT NULL
ORDER  BY (my_row).user_id;

एकल कॉलम retrieve को पुनः प्राप्त करने के लिए सुविधाजनक या पूरी पंक्ति . उदाहरण तालिका की संपूर्ण पंक्ति प्रकार का उपयोग करता है। अन्य प्रकार संभव हैं।

यह सुनिश्चित करने के लिए कि पिछले पुनरावृत्ति में एक पंक्ति पाई गई थी, एक एकल NOT NULL कॉलम (प्राथमिक कुंजी की तरह) का परीक्षण करें।

इस प्रश्न के लिए अध्याय 2ख में अधिक स्पष्टीकरण। नीचे।

संबंधित:

  • प्रति पंक्ति अंतिम N संबंधित पंक्तियों को क्वेरी करें
  • एक कॉलम के आधार पर ग्रुप करें, जबकि PostgreSQL में दूसरे कॉलम को सॉर्ट करें
<एच3>2. अलग users . के साथ टेबल

तालिका लेआउट शायद ही तब तक मायने रखता है जब तक कि प्रति प्रासंगिक user_id . में ठीक एक पंक्ति हो यह गारंटीशुदा है। उदाहरण:

CREATE TABLE users (
   user_id  serial PRIMARY KEY
 , username text NOT NULL
);

आदर्श रूप से, तालिका को log . के साथ सिंक में भौतिक रूप से क्रमबद्ध किया जाता है टेबल। देखें:

  • पोस्टग्रेज टाइमस्टैम्प क्वेरी श्रेणी को ऑप्टिमाइज़ करें

या यह काफी छोटा है (कम कार्डिनैलिटी) कि यह शायद ही मायने रखता है। अन्यथा, क्वेरी में पंक्तियों को क्रमबद्ध करने से प्रदर्शन को और अधिक अनुकूलित करने में मदद मिल सकती है। गैंग लिआंग का जोड़ देखें। यदि users . का भौतिक क्रम तालिका log पर अनुक्रमणिका से मेल खाने के लिए होती है , यह अप्रासंगिक हो सकता है।

2a. LATERAL शामिल हों

SELECT u.user_id, l.log_date, l.payload
FROM   users u
CROSS  JOIN LATERAL (
   SELECT l.log_date, l.payload
   FROM   log l
   WHERE  l.user_id = u.user_id         -- lateral reference
   AND    l.log_date <= :mydate
   ORDER  BY l.log_date DESC NULLS LAST
   LIMIT  1
   ) l;

JOIN LATERAL FROM . से पहले के संदर्भ की अनुमति देता है समान क्वेरी स्तर पर आइटम। देखें:

  • लेटरल जॉइन और PostgreSQL में सबक्वेरी में क्या अंतर है?

प्रति उपयोगकर्ता एक अनुक्रमणिका (-केवल) लुक-अप में परिणाम।

users . में अनुपलब्ध उपयोगकर्ताओं के लिए कोई पंक्ति नहीं देता है टेबल। आमतौर पर, एक विदेशी कुंजी संदर्भात्मक अखंडता को लागू करने वाली बाधा इसे खारिज कर देगी।

साथ ही, log . में मिलान प्रविष्टि के बिना उपयोगकर्ताओं के लिए कोई पंक्ति नहीं - मूल प्रश्न के अनुरूप। उन उपयोगकर्ताओं को परिणाम में रखने के लिए LEFT JOIN LATERAL ... ON true का उपयोग करें इसके बजाय CROSS JOIN LATERAL :

  • एक सरणी तर्क के साथ एक सेट-रिटर्निंग फ़ंक्शन को कई बार कॉल करें

LIMIT n . का प्रयोग करें LIMIT 1 . के बजाय एक से अधिक पंक्तियों को पुनः प्राप्त करने के लिए (लेकिन सभी नहीं) प्रति उपयोगकर्ता।

प्रभावी रूप से, ये सभी ऐसा ही करते हैं:

JOIN LATERAL ... ON true
CROSS JOIN LATERAL ...
, LATERAL ...

हालांकि, पिछले वाले की प्राथमिकता कम है। स्पष्ट JOIN अल्पविराम से पहले बांधता है। अधिक जुड़ने वाली तालिकाओं के साथ वह सूक्ष्म अंतर मायने रखता है। देखें:

  • पोस्टग्रेज क्वेरी में "तालिका के लिए FROM-खंड प्रविष्टि का अमान्य संदर्भ"
<एच4>2बी. सहसंबद्ध सबक्वेरी

एकल कॉलम retrieve को पुनः प्राप्त करने का अच्छा विकल्प एक एकल पंक्ति . से . कोड उदाहरण:

  • समूहवार अधिकतम क्वेरी अनुकूलित करें

एकाधिक कॉलम . के लिए भी ऐसा ही संभव है , लेकिन आपको अधिक स्मार्ट की आवश्यकता है:

CREATE TEMP TABLE combo (log_date date, payload int);

SELECT user_id, (combo1).*              -- note parentheses
FROM (
   SELECT u.user_id
        , (SELECT (l.log_date, l.payload)::combo
           FROM   log l
           WHERE  l.user_id = u.user_id
           AND    l.log_date <= :mydate
           ORDER  BY l.log_date DESC NULLS LAST
           LIMIT  1) AS combo1
   FROM   users u
   ) sub;

जैसे LEFT JOIN LATERAL ऊपर, इस संस्करण में सभी . शामिल हैं उपयोगकर्ता, log . में प्रविष्टियों के बिना भी . आपको NULL मिलता है combo1 . के लिए , जिसे आप WHERE . से आसानी से फ़िल्टर कर सकते हैं यदि आवश्यक हो तो बाहरी क्वेरी में क्लॉज। आपको एक NOT NULL की आवश्यकता है इस अस्पष्टता से बचने के लिए सबक्वेरी में कॉलम।

एक सहसंबद्ध उपश्रेणी केवल एक एकल मान return लौटा सकती है . आप कई स्तंभों को एक समग्र प्रकार में लपेट सकते हैं। लेकिन बाद में इसे विघटित करने के लिए, पोस्टग्रेस एक प्रसिद्ध समग्र प्रकार की मांग करता है। अनाम रिकॉर्ड को केवल एक कॉलम परिभाषा सूची प्रदान करके विघटित किया जा सकता है।
एक पंजीकृत प्रकार का उपयोग करें जैसे किसी मौजूदा तालिका के पंक्ति प्रकार का उपयोग करें। या CREATE TYPE . के साथ एक कंपोजिट टाइप को स्पष्ट रूप से (और स्थायी रूप से) रजिस्टर करें . या अस्थायी रूप से अपनी पंक्ति प्रकार को पंजीकृत करने के लिए एक अस्थायी तालिका (सत्र के अंत में स्वचालित रूप से गिरा दी गई) बनाएं। कास्ट सिंटैक्स:(log_date, payload)::combo

अंत में, हम combo1 . को विघटित नहीं करना चाहते हैं एक ही क्वेरी स्तर पर। क्वेरी प्लानर में कमजोरी के कारण यह प्रत्येक कॉलम के लिए एक बार सबक्वायरी का मूल्यांकन करेगा (अभी भी पोस्टग्रेस 12 में सच है)। इसके बजाय, इसे एक सबक्वेरी बनाएं और बाहरी क्वेरी में विघटित करें।

संबंधित:

  • प्रति समूह पहली और अंतिम पंक्ति से मान प्राप्त करें

100k लॉग प्रविष्टियों और 1k उपयोगकर्ताओं के साथ सभी 4 प्रश्नों को प्रदर्शित करना:
db<>fiddle here - पृष्ठ 11
<उप>पुराना sqlfiddle



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL और JDBC के साथ ClassNotFoundException

  2. Homebrew पोस्टग्रेज टूटा हुआ

  3. त्रुटि कैसे ठीक करें:कॉलम c.relhasoids Postgres में मौजूद नहीं है?

  4. मैं PostgreSQL 11.1 में मौजूदा कॉलम को पहचान के रूप में कैसे बदल सकता हूं?

  5. कैसे to_char () समारोह का उपयोग किए बिना PostgreSQL में तारीख से साल और महीने निकालने के लिए?