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

यादृच्छिक पंक्तियों का चयन करने का सबसे अच्छा तरीका PostgreSQL

आपके विनिर्देशों को देखते हुए (साथ ही टिप्पणियों में अतिरिक्त जानकारी),

  • आपके पास एक संख्यात्मक आईडी कॉलम (पूर्णांक संख्या) है जिसमें केवल कुछ (या मामूली कुछ) अंतराल हैं।
  • जाहिर तौर पर कुछ नहीं या कुछ लिखने की कार्रवाई।
  • आपका आईडी कॉलम अनुक्रमित किया जाना है! प्राथमिक कुंजी अच्छी तरह से काम करती है।

नीचे दी गई क्वेरी को बड़ी तालिका के अनुक्रमिक स्कैन की आवश्यकता नहीं है, केवल एक अनुक्रमणिका स्कैन की आवश्यकता है।

सबसे पहले, मुख्य क्वेरी के लिए अनुमान प्राप्त करें:

SELECT count(*) AS ct              -- optional
     , min(id)  AS min_id
     , max(id)  AS max_id
     , max(id) - min(id) AS id_span
FROM   big;

केवल संभवतः महंगा हिस्सा है count(*) (विशाल टेबल के लिए)। उपरोक्त विशिष्टताओं को देखते हुए, आपको इसकी आवश्यकता नहीं है। एक अनुमान ठीक काम करेगा, लगभग किसी भी कीमत पर उपलब्ध नहीं है (विस्तृत विवरण यहां):

SELECT reltuples AS ct FROM pg_class
WHERE oid = 'schema_name.big'::regclass;

जब तक ct ज्यादा नहीं है id_span . से छोटा , क्वेरी अन्य तरीकों से बेहतर प्रदर्शन करेगी।

WITH params AS (
   SELECT 1       AS min_id           -- minimum id <= current min id
        , 5100000 AS id_span          -- rounded up. (max_id - min_id + buffer)
    )
SELECT *
FROM  (
   SELECT p.min_id + trunc(random() * p.id_span)::integer AS id
   FROM   params p
         ,generate_series(1, 1100) g  -- 1000 + buffer
   GROUP  BY 1                        -- trim duplicates
) r
JOIN   big USING (id)
LIMIT  1000;                          -- trim surplus
  • id . में रैंडम नंबर जेनरेट करें स्थान। आपके पास "कुछ अंतराल" हैं, इसलिए पुनर्प्राप्त करने के लिए पंक्तियों की संख्या में 10% (रिक्त स्थान को आसानी से कवर करने के लिए पर्याप्त) जोड़ें।

  • प्रत्येक id संयोग से कई बार चुना जा सकता है (हालांकि एक बड़े आईडी स्थान के साथ बहुत कम संभावना है), इसलिए उत्पन्न संख्याओं को समूहित करें (या DISTINCT का उपयोग करें) )।

  • id में शामिल हों बड़ी मेज के लिए एस। इंडेक्स के साथ यह बहुत तेज़ होना चाहिए।

  • अंत में सरप्लस ट्रिम करें id s जो डुप्ली और अंतराल द्वारा नहीं खाया गया है। प्रत्येक पंक्ति में पूरी तरह समान अवसर . है चुना जाना है।

लघु संस्करण

आप सरल कर सकते हैं यह प्रश्न। उपरोक्त प्रश्न में CTE केवल शैक्षिक उद्देश्यों के लिए है:

SELECT *
FROM  (
   SELECT DISTINCT 1 + trunc(random() * 5100000)::integer AS id
   FROM   generate_series(1, 1100) g
   ) r
JOIN   big USING (id)
LIMIT  1000;

rCTE से परिष्कृत करें

खासकर यदि आप अंतराल और अनुमानों के बारे में इतने निश्चित नहीं हैं।

WITH RECURSIVE random_pick AS (
   SELECT *
   FROM  (
      SELECT 1 + trunc(random() * 5100000)::int AS id
      FROM   generate_series(1, 1030)  -- 1000 + few percent - adapt to your needs
      LIMIT  1030                      -- hint for query planner
      ) r
   JOIN   big b USING (id)             -- eliminate miss

   UNION                               -- eliminate dupe
   SELECT b.*
   FROM  (
      SELECT 1 + trunc(random() * 5100000)::int AS id
      FROM   random_pick r             -- plus 3 percent - adapt to your needs
      LIMIT  999                       -- less than 1000, hint for query planner
      ) r
   JOIN   big b USING (id)             -- eliminate miss
   )
TABLE  random_pick
LIMIT  1000;  -- actual limit

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

डुप्लिकेट को UNION . द्वारा हटा दिया जाता है आरसीटीई में।

बाहरी LIMIT हमारे पास पर्याप्त पंक्तियाँ होते ही CTE को रोक देता है।

यह क्वेरी उपलब्ध इंडेक्स का उपयोग करने के लिए सावधानीपूर्वक तैयार की गई है, वास्तव में यादृच्छिक पंक्तियां उत्पन्न करती है और तब तक नहीं रुकती जब तक हम सीमा को पूरा नहीं करते (जब तक कि रिकर्सन सूख न जाए)। यदि आप इसे फिर से लिखने जा रहे हैं तो यहां कई नुकसान हैं।

समारोह में लपेटें

अलग-अलग मापदंडों के साथ बार-बार उपयोग के लिए:

CREATE OR REPLACE FUNCTION f_random_sample(_limit int = 1000, _gaps real = 1.03)
  RETURNS SETOF big
  LANGUAGE plpgsql VOLATILE ROWS 1000 AS
$func$
DECLARE
   _surplus  int := _limit * _gaps;
   _estimate int := (           -- get current estimate from system
      SELECT c.reltuples * _gaps
      FROM   pg_class c
      WHERE  c.oid = 'big'::regclass);
BEGIN
   RETURN QUERY
   WITH RECURSIVE random_pick AS (
      SELECT *
      FROM  (
         SELECT 1 + trunc(random() * _estimate)::int
         FROM   generate_series(1, _surplus) g
         LIMIT  _surplus           -- hint for query planner
         ) r (id)
      JOIN   big USING (id)        -- eliminate misses

      UNION                        -- eliminate dupes
      SELECT *
      FROM  (
         SELECT 1 + trunc(random() * _estimate)::int
         FROM   random_pick        -- just to make it recursive
         LIMIT  _limit             -- hint for query planner
         ) r (id)
      JOIN   big USING (id)        -- eliminate misses
   )
   TABLE  random_pick
   LIMIT  _limit;
END
$func$;

कॉल करें:

SELECT * FROM f_random_sample();
SELECT * FROM f_random_sample(500, 1.05);

आप इस जेनेरिक को किसी भी तालिका के लिए काम करने के लिए भी बना सकते हैं:पीके कॉलम और टेबल का नाम पॉलिमॉर्फिक प्रकार के रूप में लें और EXECUTE का उपयोग करें ... लेकिन यह इस प्रश्न के दायरे से बाहर है। देखें:

  • एक PL/pgSQL फ़ंक्शन को रिफ़ैक्टर करें ताकि विभिन्न SELECT क्वेरीज़ का आउटपुट लौटाया जा सके

संभावित विकल्प

यदि आपकी आवश्यकताएं दोहराए जाने के लिए समान सेट की अनुमति देती हैं कॉल (और हम बार-बार कॉल करने के बारे में बात कर रहे हैं) मैं एक भौतिक दृश्य पर विचार करूंगा . उपरोक्त क्वेरी को एक बार निष्पादित करें और परिणाम को तालिका में लिखें। उपयोगकर्ताओं को बिजली की गति से अर्ध यादृच्छिक चयन मिलता है। अंतराल या अपनी पसंद की घटनाओं पर अपने यादृच्छिक चयन को ताज़ा करें।

पोस्टग्रेज 9.5 में TABLESAMPLE SYSTEM (n) का परिचय दिया गया है

जहां n एक प्रतिशत है। मैनुअल:

<ब्लॉकक्वॉट>

BERNOULLI और SYSTEM नमूनाकरण विधियां प्रत्येक एक एकल तर्क को स्वीकार करती हैं जो नमूना से तालिका का अंश है, जिसे 0 और 100 के बीच प्रतिशत के रूप में व्यक्त किया जाता है . यह तर्क कोई भी हो सकता है real -मूल्यवान अभिव्यक्ति।

बोल्ड जोर मेरा। यह बहुत तेज़ है , लेकिन परिणाम बिल्कुल यादृच्छिक नहीं है . मैनुअल फिर से:

<ब्लॉकक्वॉट>

SYSTEM विधि BERNOULLI . से काफी तेज है विधिजब छोटे नमूने प्रतिशत निर्दिष्ट किए जाते हैं, लेकिन यह क्लस्टरिंग प्रभावों के परिणामस्वरूप तालिका के बिना-यादृच्छिक नमूना लौटा सकता है।

लौटाई गई पंक्तियों की संख्या बेतहाशा भिन्न हो सकती है। हमारे उदाहरण के लिए, मोटे तौर पर . प्राप्त करने के लिए 1000 पंक्तियाँ:

SELECT * FROM big TABLESAMPLE SYSTEM ((1000 * 100) / 5100000.0);

संबंधित:

  • PostgreSQL में किसी तालिका की पंक्ति गणना खोजने का तेज़ तरीका

या अतिरिक्त मॉड्यूल स्थापित करें tsm_system_rows अनुरोधित पंक्तियों की संख्या प्राप्त करने के लिए (यदि पर्याप्त हैं) और अधिक सुविधाजनक सिंटैक्स की अनुमति दें:

SELECT * FROM big TABLESAMPLE SYSTEM_ROWS(1000);

विवरण के लिए इवान का उत्तर देखें।

लेकिन यह अभी भी बिल्कुल यादृच्छिक नहीं है।



  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 कथन - स्तंभ नाम मौजूद नहीं है

  2. PostgreSQL में एक तिथि में वर्ष जोड़ें

  3. PostgreSQL में IN बनाम कोई भी ऑपरेटर

  4. PostgreSQL के लिए pgBouncer का उपयोग करने के लिए एक गाइड

  5. Django के सिंकडब को चलाते समय OSX 10.7.3 पर पोस्टग्रेस्क्ल सॉकेट त्रुटि