आपके विनिर्देशों को देखते हुए (साथ ही टिप्पणियों में अतिरिक्त जानकारी),
- आपके पास एक संख्यात्मक आईडी कॉलम (पूर्णांक संख्या) है जिसमें केवल कुछ (या मामूली कुछ) अंतराल हैं।
- जाहिर तौर पर कुछ नहीं या कुछ लिखने की कार्रवाई।
- आपका आईडी कॉलम अनुक्रमित किया जाना है! प्राथमिक कुंजी अच्छी तरह से काम करती है।
नीचे दी गई क्वेरी को बड़ी तालिका के अनुक्रमिक स्कैन की आवश्यकता नहीं है, केवल एक अनुक्रमणिका स्कैन की आवश्यकता है।
सबसे पहले, मुख्य क्वेरी के लिए अनुमान प्राप्त करें:
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);
विवरण के लिए इवान का उत्तर देखें।
लेकिन यह अभी भी बिल्कुल यादृच्छिक नहीं है।