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


345

मैं PostgreSQL में पंक्तियों का एक यादृच्छिक चयन चाहता हूं, मैंने यह कोशिश की:

select * from table where random() < 0.01;

लेकिन कुछ अन्य इसकी सलाह देते हैं:

select * from table order by random() limit 1000;

मेरे पास 500 मिलियन पंक्तियों के साथ एक बहुत बड़ी मेज है, मैं चाहता हूं कि यह तेज हो।

कौन सा दृष्टिकोण बेहतर है? क्या अंतर हैं? यादृच्छिक पंक्तियों का चयन करने का सबसे अच्छा तरीका क्या है?


1
हाय जैक, आपकी प्रतिक्रिया के लिए धन्यवाद, निष्पादन का समय क्रम में धीमा है, लेकिन मैं यह जानना चाहूंगा कि यदि कोई अलग है ...
नैनोउन्यूज

उह्ह्ह… आपका स्वागत है। तो, क्या आपने अलग-अलग तरीकों से बेंचमार्किंग की कोशिश की है?

बहुत तेज़ तरीके भी हैं। यह सब आपकी आवश्यकताओं पर निर्भर करता है और आपको किसके साथ काम करना है। क्या आपको 1000 पंक्तियों की आवश्यकता है? क्या तालिका में एक संख्यात्मक आईडी है? नहीं / कुछ / कई अंतराल के साथ? गति कितनी महत्वपूर्ण है? समय इकाई प्रति कितने अनुरोध? क्या प्रत्येक अनुरोध को एक अलग सेट की आवश्यकता होती है या वे निर्धारित समय स्लाइस के लिए समान हो सकते हैं?
एरविन ब्रान्डेसटेटर

6
पहला विकल्प "(यादृच्छिक () <0.01)" गणितीय रूप से गलत है क्योंकि आपको प्रतिक्रिया में कोई पंक्तियाँ नहीं मिल सकती हैं यदि कोई यादृच्छिक संख्या 0.01 से कम नहीं है, तो यह किसी भी मामले में हो सकता है (भले ही कम संभावना हो), चाहे तालिका कितनी भी बड़ी हो। या उच्चतर सीमा। दूसरा विकल्प हमेशा सही होता है
हर्मी

1
यदि आप केवल एक पंक्ति का चयन करना चाहते हैं, तो इस प्रश्न को देखें: stackoverflow.com/q/5297396/247696
फ्लिम

जवाबों:


230

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

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

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

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

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जो कि डिप और गैप द्वारा नहीं खाया गया है। हर पंक्ति को पूरी तरह से चुने जाने का एक समान मौका है।

लघु संस्करण

आप इस क्वेरी को सरल बना सकते हैं । उपरोक्त क्वेरी में 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
   )
SELECT *
FROM   random_pick
LIMIT  1000;  -- actual limit

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

UNIONRCTE द्वारा डुप्लिकेट को समाप्त कर दिया जाता है ।

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

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

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

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

CREATE OR REPLACE FUNCTION f_random_sample(_limit int = 1000, _gaps real = 1.03)
  RETURNS SETOF big 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
   )
   SELECT *
   FROM   random_pick
   LIMIT  _limit;
END
$func$  LANGUAGE plpgsql VOLATILE ROWS 1000;

कॉल करें:

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

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

संभव विकल्प

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

9.5 का परिचय देता है TABLESAMPLE SYSTEM (n)

कहां nप्रतिशत है। नियम पुस्तिका:

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

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

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

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

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

सम्बंधित:

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

SELECT * FROM big TABLESAMPLE SYSTEM_ROWS(1000);

देखें इवान के जवाब जानकारी के लिए।

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


टी टेबल को कहाँ परिभाषित किया गया है ? टी के बजाय आर होना चाहिए ?
ल्यूक एम

1
@ ल्यूक: इसे यहां परिभाषित किया गया है: JOIN bigtbl tजो कि कम है JOIN bigtbl AS ttके लिए एक तालिका उपनाम है bigtbl। इसका उद्देश्य सिंटैक्स को छोटा करना है लेकिन इस विशेष मामले में इसकी आवश्यकता नहीं होगी। मैंने अपने उत्तर में प्रश्न को सरल बनाया और एक सरल संस्करण जोड़ा।
इरविन ब्रान्डसेट्टर

Gener_series (1,1100) से मानों की श्रेणी का उद्देश्य क्या है?
विस्मय-ओ

@ विस्मयकारी-ओ: लक्ष्य 1000 पंक्तियों को पुनः प्राप्त करना है, मैं कुछ अंतरालों या (बिना संभव लेकिन संभव) डुप्लिकेट रैंडम नंबरों की भरपाई के लिए एक अतिरिक्त 10% से शुरू करता हूं ... स्पष्टीकरण मेरे उत्तर में है।
एरविन ब्रान्डसेट्टर

इरविन, मैंने आपके "संभावित विकल्प" की एक विविधता पोस्ट की: stackoverflow.com/a/23634212/430128 । आपके विचारों में रुचि होगी।
रमन

100

आप उपयोग करके दोनों की निष्पादन योजना की जांच और तुलना कर सकते हैं

EXPLAIN select * from table where random() < 0.01;
EXPLAIN select * from table order by random() limit 1000;

एक बड़ी तालिका 1 पर एक त्वरित परीक्षण से पता चलता है, कि ORDER BYपहले पूरी तालिका को छांटा जाता है और फिर पहले 1000 वस्तुओं को चुना जाता है। एक बड़ी तालिका को छाँटने से न केवल उस तालिका को पढ़ा जाता है, बल्कि अस्थायी फ़ाइलों को पढ़ना और लिखना भी शामिल है। where random() < 0.1केवल एक बार पूरा तालिका स्कैन करता है।

बड़ी तालिकाओं के लिए यह वह नहीं हो सकता है जो आप चाहते हैं कि एक पूर्ण तालिका स्कैन में भी समय लग सकता है।

एक तीसरा प्रस्ताव होगा

select * from table where random() < 0.01 limit 1000;

जैसे ही 1000 पंक्तियाँ मिलीं यह टेबल स्कैन रोक देता है और इसलिए जल्दी लौटता है। बेशक यह यादृच्छिकता को थोड़ा कम करता है, लेकिन शायद यह आपके मामले में काफी अच्छा है।

संपादित करें: इस विचार के अलावा, आप इसके लिए पहले से ही पूछे गए प्रश्नों की जांच कर सकते हैं। क्वेरी का उपयोग करके [postgresql] randomकुछ हिट मिलते हैं।

और कई और अधिक दृष्टिकोणों को रेखांकित करते हुए डिपेज़ का एक जुड़ा हुआ लेख:


1 "बड़े" के रूप में "पूर्ण तालिका स्मृति में फिट नहीं होगी"।


1
आदेश देने के लिए अस्थायी फ़ाइल लिखने के बारे में अच्छी बात है। यह वास्तव में एक बड़ी हिट है। मुझे लगता है कि हम कर सकते हैं random() < 0.02और फिर उस सूची में फेरबदल, फिर limit 1000! सॉर्ट कुछ हजार पंक्तियों (लोल) पर कम खर्चीला होगा।
डोनाल्ड माइनर

"चयन करें" तालिका से जहां यादृच्छिक () <0.05 सीमा 500; " Postgresql के लिए आसान तरीकों में से एक है। हमने अपनी एक परियोजना में इसका उपयोग किया जहां हमें 5% परिणामों का चयन करने की आवश्यकता थी और प्रसंस्करण के लिए एक बार में 500 पंक्तियों से अधिक नहीं।
thaharold

क्यों दुनिया में आप कभी भी एक 500 मीटर पंक्ति तालिका पर एक नमूना प्राप्त करने के लिए ओ (एन) पूर्ण स्कैन पर विचार करेंगे? यह बड़े तालिकाओं पर हास्यास्पद रूप से धीमा है और पूरी तरह से अनावश्यक है।
मफू

76

यादृच्छिक द्वारा पोस्टग्रेज़ल ऑर्डर (), यादृच्छिक क्रम में पंक्तियों का चयन करें:

select your_columns from your_table ORDER BY random()

रैंडम द्वारा पोस्टग्रैस्कल ऑर्डर () एक अलग के साथ:

select * from 
  (select distinct your_columns from your_table) table_alias
ORDER BY random()

यादृच्छिक सीमा एक पंक्ति द्वारा पोस्टग्रैस्कल ऑर्डर:

select your_columns from your_table ORDER BY random() limit 1

1
select your_columns from your_table ORDER BY random() limit 145 मिनट की पंक्तियों पर अमल करने के लिए ~ 2 मिनट का समय लें
nguyên

क्या इसमें तेजी लाने का कोई तरीका है?
CpILL

43

PostgreSQL 9.5 के साथ शुरू, एक टेबल से यादृच्छिक तत्वों को प्राप्त करने के लिए समर्पित एक नया वाक्यविन्यास है:

SELECT * FROM mytable TABLESAMPLE SYSTEM (5);

यह उदाहरण आपको 5% तत्वों से देगा mytable

इस ब्लॉग पोस्ट के बारे में अधिक विवरण देखें: http://www.postgresql.org/docs/current/static/sql-select.html


3
डॉक्स से एक महत्वपूर्ण नोट: "सिस्टम विधि प्रत्येक ब्लॉक के साथ ब्लॉक-स्तरीय नमूने का चयन करती है, जिसमें चयनित होने का निर्दिष्ट मौका होता है। प्रत्येक चयनित ब्लॉक में सभी पंक्तियों को वापस कर दिया जाता है। छोटे नमूने के परीक्षण के समय सिस्टम विधि बर्नोलाई विधि की तुलना में काफी तेज होती है। निर्दिष्ट हैं, लेकिन यह क्लस्टरिंग प्रभावों के परिणामस्वरूप तालिका के कम-यादृच्छिक नमूने को वापस कर सकता है। "
टिम

1
क्या प्रतिशत के बजाय कई पंक्तियों को निर्दिष्ट करने का तरीका है?
फ्लि‍म

4
आप TABLESAMPLE SYSTEM_ROWS(400)400 यादृच्छिक पंक्तियों का एक नमूना प्राप्त करने के लिए उपयोग कर सकते हैं । आपको इस कथन का उपयोग करने के लिए अंतर्निहित tsm_system_rowsएक्सटेंशन को सक्षम करने की आवश्यकता है ।
मिकाल ले बालिफ़

27

आदेश के साथ एक धीमी एक होने जा रहा है।

select * from table where random() < 0.01;रिकॉर्ड से रिकॉर्ड जाता है, और इसे बेतरतीब ढंग से फ़िल्टर करने या न करने का फैसला करता है। यह होने जा रहा है O(N)क्योंकि इसे केवल प्रत्येक रिकॉर्ड को एक बार जांचना होगा।

select * from table order by random() limit 1000;पूरी तालिका को क्रमबद्ध करने जा रहा है, फिर पहले 1000 को चुनें। पर्दे के पीछे किसी जादू जादू के अलावा, यह क्रम है O(N * log N)

एक के लिए नकारात्मक पक्ष यह random() < 0.01है कि आपको आउटपुट रिकॉर्ड की एक चर संख्या मिलेगी।


ध्यान दें, यादृच्छिक द्वारा सॉर्ट करने की तुलना में डेटा का एक सेट फेरबदल करने का एक बेहतर तरीका है: फिशर-येट्स शफल , जो अंदर चलता है O(N)। SQL में फेरबदल को लागू करना काफी चुनौती जैसा लगता है, हालाँकि।


3
कोई कारण नहीं है कि आप अपने पहले उदाहरण के अंत में एक सीमा 1 नहीं जोड़ सकते। केवल समस्या यह है कि आपको कोई रिकॉर्ड वापस नहीं मिलेगा, इसलिए आपको अपने कोड में विचार करना होगा।
Relequestual

फिशर-येट्स के साथ परेशानी यह है कि आपको इसे चुनने के लिए पूरे डेटासेट को मेमोरी में रखना होगा। बहुत बड़े डेटासेट के लिए संभव नहीं है :(
CpILL

16

यहाँ एक निर्णय है जो मेरे लिए काम करता है। मुझे लगता है कि इसे समझना और निष्पादित करना बहुत सरल है।

SELECT 
  field_1, 
  field_2, 
  field_2, 
  random() as ordering
FROM 
  big_table
WHERE 
  some_conditions
ORDER BY
  ordering 
LIMIT 1000;

6
मुझे लगता है कि यह समाधान काम कर रहा है ORDER BY random()जो काम करता है लेकिन बड़ी तालिका के साथ काम करते समय कुशल नहीं हो सकता है।
अन काओ

15
select * from table order by random() limit 1000;

यदि आप जानते हैं कि आप कितनी पंक्तियाँ चाहते हैं, तो देखें tsm_system_rows

tsm_system_rows

मॉड्यूल सारणी नमूना विधि प्रदान करता है System_ROWS, जिसका उपयोग एक सेलेक्ट कमांड के TABLESAMPLE क्लॉज में किया जा सकता है।

यह तालिका नमूनाकरण विधि एक एकल पूर्णांक तर्क को स्वीकार करती है जो पढ़ने के लिए पंक्तियों की अधिकतम संख्या है। परिणामी नमूने में हमेशा वही पंक्तियाँ होंगी, जब तक कि तालिका में पर्याप्त पंक्तियाँ न हों, जिस स्थिति में पूरी तालिका चुनी जाती है। बिल्ट-इन सिस्टम सैंपलिंग विधि की तरह, System_ROWS ब्लॉक-लेवल सैंपलिंग करता है, ताकि सैंपल पूरी तरह से रैंडम न हो, लेकिन क्लस्टरिंग इफेक्ट्स के अधीन हो सकता है, खासकर अगर केवल थोड़ी संख्या में पंक्तियों का अनुरोध किया जाए।

पहले एक्सटेंशन स्थापित करें

CREATE EXTENSION tsm_system_rows;

फिर आपकी क्वेरी,

SELECT *
FROM table
TABLESAMPLE SYSTEM_ROWS(1000);

2
मैंने आपके अतिरिक्त उत्तर के लिए एक लिंक जोड़ा, यह अंतर्निहित SYSTEMपद्धति पर एक उल्लेखनीय सुधार है ।
एरविन ब्रान्डसेट्टर

मैंने यहां केवल एक प्रश्न (यादृच्छिक एकल रिकॉर्ड) का जवाब दिया है, जिसके दौरान मैंने और बेंचमार्किंगtsm_system_rows और tsm_system_timeएक्सटेंशन का परीक्षण किया । जहाँ तक मैं देख सकता हूँ, वे लगभग किसी भी चीज़ के लिए बेकार हैं लेकिन यादृच्छिक पंक्तियों का बिल्कुल न्यूनतम चयन। यदि आप मेरे विश्लेषण की वैधता या अन्यथा पर त्वरित रूप से टिप्पणी कर सकते हैं तो मैं आभारी रहूंगा।
18

6

यदि आप सिर्फ एक पंक्ति चाहते हैं, तो आप offsetप्राप्त की गई गणना का उपयोग कर सकते हैं count

select * from table_name limit 1
offset floor(random() * (select count(*) from table_name));

2

Erwin Brandstetter द्वारा उल्लिखित भौतिक दृष्टिकोण "संभावित विकल्प" की विविधता संभव है।

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

यह मानते हुए कि इनपुट तालिका है:

id_values  id  |   used
           ----+--------
           1   |   FALSE
           2   |   FALSE
           3   |   FALSE
           4   |   FALSE
           5   |   FALSE
           ...

ID_VALUESआवश्यकतानुसार तालिका को आबाद करें । फिर, जैसा कि इरविन ने कहा है, एक भौतिकवादी दृश्य बनाएं जो ID_VALUESतालिका को एक बार यादृच्छिक करता है :

CREATE MATERIALIZED VIEW id_values_randomized AS
  SELECT id
  FROM id_values
  ORDER BY random();

ध्यान दें कि भौतिक दृश्य में उपयोग किए गए कॉलम नहीं हैं, क्योंकि यह जल्दी से पुराना हो जाएगा। न ही दृश्य में अन्य स्तंभों को शामिल करने की आवश्यकता है जो id_valuesतालिका में हो सकते हैं ।

आदेश प्राप्त करने के लिए (और "उपभोग") यादृच्छिक मान, एक पर अद्यतन लौटने का उपयोग id_values, का चयन करने id_valuesसे id_values_randomizedएक में शामिल होने के साथ, और केवल प्रासंगिक संभावनाओं प्राप्त करने के लिए वांछित मानदंड लागू करने। उदाहरण के लिए:

UPDATE id_values
SET used = TRUE
WHERE id_values.id IN 
  (SELECT i.id
    FROM id_values_randomized r INNER JOIN id_values i ON i.id = r.id
    WHERE (NOT i.used)
    LIMIT 5)
RETURNING id;

LIMITआवश्यक रूप से बदलें - यदि आपको एक समय में केवल एक यादृच्छिक मूल्य की आवश्यकता है, तो बदल LIMITदें 1

उचित अनुक्रमणिका के साथ id_values, मेरा मानना ​​है कि UPDATE-RETURNING को बहुत कम लोड के साथ जल्दी से निष्पादित करना चाहिए। यह एक डेटाबेस राउंड ट्रिप के साथ यादृच्छिक मान लौटाता है। "पात्र" पंक्तियों के मानदंड आवश्यकतानुसार जटिल हो सकते हैं। नई पंक्तियों को id_valuesकिसी भी समय तालिका में जोड़ा जा सकता है , और जैसे ही भौतिक दृश्य ताज़ा हो जाएगा (जो कि संभवत: एक ऑफ-पीक समय में चलाया जा सकता है) वे अनुप्रयोग के लिए सुलभ हो जाएंगे। भौतिकवादी दृश्य का निर्माण और ताज़ा धीमी गति से होगा, लेकिन इसे केवल तब निष्पादित करना होगा जब नई आईडी को id_valuesतालिका में जोड़ा जाए ।


बहुत ही रोचक। क्या यह काम करेगा, अगर मुझे न केवल चयन करने की आवश्यकता है, बल्कि चयन का उपयोग करके अपडेट भी करना है .. pg_try_advisory_xact_lock के साथ अपडेट करें? (यानी मुझे कई समवर्ती पढ़ने और लिखने की ज़रूरत है)
मैथ्यू

1

मेरे अनुभव से एक सबक:

offset floor(random() * N) limit 1से तेज नहीं है order by random() limit 1

मुझे लगा कि offsetएप्रोच तेज़ होगा क्योंकि उसे पोस्टग्रैज़ में छँटाई के समय को बचाना चाहिए। पता चला कि यह नहीं था।


0

rटाइप के साथ एक कॉलम जोड़ें serial। सूचकांक r

मान लें कि हमारे पास 200,000 पंक्तियाँ हैं, हम एक यादृच्छिक संख्या उत्पन्न करने जा रहे हैं n, जहाँ 0 << n200, 000 है।

पंक्तियों का चयन करें r > n, उन्हें क्रमबद्ध करें ASCऔर सबसे छोटी को चुनें।

कोड:

select * from YOUR_TABLE 
where r > (
    select (
        select reltuples::bigint AS estimate
        from   pg_class
        where  oid = 'public.YOUR_TABLE'::regclass) * random()
    )
order by r asc limit(1);

कोड आत्म-व्याख्यात्मक है। बीच में उप-वर्ग का उपयोग जल्दी से https://stackoverflow.com/a/7945274/1271094 से तालिका पंक्ति की गणना का अनुमान लगाने के लिए किया जाता है ।

आवेदन स्तर में आपको कथन को फिर से निष्पादित करना होगा अगर n> पंक्तियों की संख्या या कई पंक्तियों का चयन करने की आवश्यकता है।


मुझे यह पसंद है क्योंकि यह छोटा और सुरुचिपूर्ण है :) और मुझे इसे बेहतर बनाने का एक तरीका भी मिला: EXPLAIN ANALYZE मुझसे कहता है कि इस तरह, PKEY इंडेक्स का उपयोग नहीं किया जाएगा क्योंकि यादृच्छिक () एक डबल रिटर्न करता है, जबकि PKEY को एक BIGINT की आवश्यकता होती है।
fxtentacle

जहां से आपका चयन होता है, वहां से r> चुनें (चयन करें reltuples :: bigint AS अनुमान से pg_class जहां oid = 'public.YOUR_TABLE' :: regclass) * random ()) :: आरजीसी सीमा के साथ BIGINT ऑर्डर (1);
15

0

मुझे पता है कि मुझे पार्टी में थोड़ी देर हो गई है, लेकिन मुझे सिर्फ pg_sample नामक यह भयानक उपकरण मिला है :

pg_sample - संदर्भात्मक अखंडता को बनाए रखते हुए एक बड़े PostgreSQL डेटाबेस से एक छोटा, नमूना डेटासेट निकालें।

मैं एक 350M पंक्तियों डेटाबेस के साथ यह कोशिश की और यह वास्तव में तेजी से था, यादृच्छिकता के बारे में पता नहीं है ।

./pg_sample --limit="small_table = *" --limit="large_table = 100000" -U postgres source_db | psql -U postgres target_db
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.