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