2012-05-15 26 views
9

मेरे पास दो टेबल हैं, custassets और tags। कुछ टेस्ट डेटा जेनरेट करने के लिए मैं INSERT INTOSELECT के साथ कई से अधिक टेबल करना चाहता हूं जो प्रत्येक से यादृच्छिक पंक्तियां प्राप्त करता है (ताकि एक तालिका से यादृच्छिक प्राथमिक कुंजी दूसरे से यादृच्छिक प्राथमिक कुंजी के साथ जोड़ा जा सके) । मेरे आश्चर्य के लिए यह उतना आसान नहीं है जितना मैंने पहले सोचा था, इसलिए मैं खुद को सिखाने के लिए इसके साथ रह रहा हूं।मैं PostgreSQL में एक यादृच्छिक कार्टशियन उत्पाद कैसे प्राप्त कर सकता हूं?

मेरा पहला प्रयास यहां है। मैं 10 custassets और 3 tags का चयन करता हूं, लेकिन दोनों मामले में समान हैं। मैं ठीक होने वाली पहली तालिका के साथ ठीक हूं, लेकिन मैं असाइन किए गए टैग को यादृच्छिक बनाना चाहता हूं।

SELECT 
    custassets_rand.id custassets_id, 
    tags_rand.id tags_rand_id 
FROM 
    (
     SELECT id FROM custassets WHERE defunct = false ORDER BY RANDOM() LIMIT 10 
    ) AS custassets_rand 
, 
    (
     SELECT id FROM tags WHERE defunct = false ORDER BY RANDOM() LIMIT 3 
    ) AS tags_rand 

यह पैदा करता है:

custassets_id | tags_rand_id 
---------------+-------------- 
      9849 |   3322 } 
      9849 |   4871 } this pattern of tag PKs is repeated 
      9849 |   5188 } 
     12145 |   3322 
     12145 |   4871 
     12145 |   5188 
     17837 |   3322 
     17837 |   4871 
     17837 |   5188 
.... 

मैं तो निम्नलिखित दृष्टिकोण की कोशिश की: SELECT स्तंभ सूची में दूसरे नंबर पर RANDOM() कॉल कर। हालांकि यह एक और भी बदतर था, क्योंकि यह एक टैग पीके और इसके साथ चिपकता है।

SELECT 
    custassets_rand.id custassets_id, 
    (SELECT id FROM tags WHERE defunct = false ORDER BY RANDOM() LIMIT 1) tags_rand_id 
FROM 
    (
     SELECT id FROM custassets WHERE defunct = false ORDER BY RANDOM() LIMIT 30 
    ) AS custassets_rand 

परिणाम:

custassets_id | tags_rand_id 
---------------+-------------- 
     16694 |   1537 
     14204 |   1537 
     23823 |   1537 
     34799 |   1537 
     36388 |   1537 
.... 

यह एक पटकथा भाषा में आसान होगा, और मुझे यकीन है कि एक संग्रहीत प्रक्रिया या अस्थायी तालिका के साथ काफी आसानी से किया जा सकता हूँ। लेकिन क्या मैं इसे INSERT INTO SELECT के साथ कर सकता हूं?

मैंने यादृच्छिक फ़ंक्शन का उपयोग करके पूर्णांक प्राथमिक कुंजी चुनने के बारे में सोचा था, लेकिन दुर्भाग्य से दोनों तालिकाओं के लिए प्राथमिक कुंजी में वृद्धि अनुक्रमों में अंतर होता है (और इसलिए प्रत्येक तालिका में एक खाली पंक्ति चुनी जा सकती है)। यह अन्यथा ठीक होगा!

+0

टिप्पणी करने वाले सभी लोगों के लिए धन्यवाद - अगर यह मेरे ऊपर था, तो मैं कई टिकों से सम्मानित होता! ':-)' – halfer

उत्तर

11

सीटीई को उप-सामानों के साथ बदलने के लिए अपडेट किया गया जो आमतौर पर तेज़ होते हैं।

सही मायने में यादृच्छिक संयोजन का उत्पादन करने के लिए, यह बड़ा सेट के लिए rn randomize करने के लिए काफी है:

SELECT c_id, t_id 
FROM (
    SELECT id AS c_id, row_number() OVER (ORDER BY random()) AS rn 
    FROM custassets 
    ) x 
JOIN (SELECT id AS t_id, row_number() OVER() AS rn FROM tags) y USING (rn); 

तो मनमाना संयोजन काफी अच्छा कर रहे हैं, यह तेजी से (बड़े तालिकाओं के लिए विशेष रूप से) है:

SELECT c_id, t_id 
FROM (SELECT id AS c_id, row_number() OVER() AS rn FROM custassets) x 
JOIN (SELECT id AS t_id, row_number() OVER() AS rn FROM tags) y USING (rn); 

यदि दोनों तालिकाओं में पंक्तियों की संख्या मेल नहीं खाती है और आप बड़े टा से पंक्तियां खोना नहीं चाहते हैं ble, छोटे टेबल कई बार से पंक्तियों में शामिल होने के modulo operator % का उपयोग करें:

SELECT c_id, t_id 
FROM (
    SELECT id AS c_id, row_number() OVER() AS rn 
    FROM custassets -- table with fewer rows 
    ) x 
JOIN (
    SELECT id AS t_id, (row_number() OVER() % small.ct) + 1 AS rn 
    FROM tags 
     , (SELECT count(*) AS ct FROM custassets) AS small 
    ) y USING (rn); 

मेरी टिप्पणी में उल्लेख किया है, window functions (with appended OVER clause) PostgreSQL 8.4 या बाद में उपलब्ध हैं।

+0

इरविन, आपके पूर्ण उत्तर के लिए धन्यवाद - बहुत सराहना की। अब मुझे 'साथ' और 'उपयोग' भी देखना चाहिए! ':)' – halfer

+0

@ हेलफर: कोई चिंता नहीं, दोनों समझने में आसान हैं। सीटीई मूल रूप से सबक्वायरीज़ का उपयोग किया जा सकता है जिसे कई बार इस्तेमाल किया जा सकता है और 'यूएसएन (आरएन) 'मूल रूप से' चालू x.rn = y.rn' के लिए छोटा है। हालांकि, सूक्ष्म मतभेद हैं। बस मेरे लिंक का पालन करें। –

1

यह मुझे बताता है कि इन सभी वर्षों के संबंधपरक डेटाबेस के बाद, इस तरह की चीजों को करने के बहुत अच्छे क्रॉस डेटाबेस तरीके प्रतीत नहीं होते हैं। एमएसडीएन लेख http://msdn.microsoft.com/en-us/library/cc441928.aspx में कुछ दिलचस्प विचार हैं, लेकिन निश्चित रूप से यह PostgreSQL नहीं है। और फिर भी, उनके समाधान के लिए एक ही पास की आवश्यकता होती है, जब मुझे लगता है कि इसे स्कैन के बिना किया जाना चाहिए।

मैं कुछ तरीकों की कल्पना कर सकता हूं जो बिना किसी पास (चयन में) काम कर सकते हैं, लेकिन इसमें एक और टेबल बनाना शामिल होगा जो आपकी तालिका की प्राथमिक कुंजी को यादृच्छिक संख्याओं (या रैखिक अनुक्रमों के लिए मैप करता है जो बाद में यादृच्छिक रूप से चुनते हैं) कुछ तरीकों से वास्तव में बेहतर हो सकता है), और निश्चित रूप से, इसमें भी समस्याएं हो सकती हैं।

मुझे एहसास है कि यह शायद एक गैर-उपयोगी टिप्पणी है, मुझे लगा कि मुझे थोड़ी देर रुकने की जरूरत है।

+0

हे, ठीक है, अगर उत्तर 'संभव नहीं है', तो यह पर्याप्त है ':) '। हम देखेंगे कि अन्य उत्तरों क्या आते हैं। – halfer

+0

वास्तव में, मैं यह भी देखना चाहता हूं कि अन्य उत्तरों क्या आते हैं। मेरा मतलब यह नहीं था कि जवाब संभव नहीं है, मेरा मतलब है कि किसी विशेष समाधान में "अच्छा नहीं" या तो बहुत सारे सेटअप या पूर्ण टेबल स्कैन के पास लगने लगते हैं। मुझे यह मानना ​​है कि मुझे यकीन नहीं है कि आपकी क्वेरी में क्या गलत है। – JayC

+3

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

1

यदि आप बस प्रत्येक तरफ से पंक्तियों का एक यादृच्छिक सेट प्राप्त करना चाहते हैं, तो छद्म-यादृच्छिक संख्या जेनरेटर का उपयोग करें।

select * 
from (select a.*, row_number() over (order by NULL) as rownum -- NULL may not work, "(SELECT NULL)" works in MSSQL 
     from a 
    ) a cross join 
    (select b.*, row_number() over (order by NULL) as rownum 
     from b 
    ) b 
where a.rownum <= 30 and b.rownum <= 30 

यह एक कार्तीय उत्पाद है, जो एक मानते हुए 900 पंक्तियों वापस आती है और ख प्रत्येक कम से कम 30 पंक्तियों कर रहा है: मैं की तरह कुछ का प्रयोग करेंगे।

हालांकि, मैंने आपके प्रश्न को यादृच्छिक संयोजन के रूप में व्याख्या की। एक बार फिर, मैं छद्म-यादृच्छिक दृष्टिकोण के लिए जाना होगा।

select * 
from (select a.*, row_number() over (order by NULL) as rownum -- NULL may not work, "(SELECT NULL)" works in MSSQL 
     from a 
    ) a cross join 
    (select b.*, row_number() over (order by NULL) as rownum 
     from b 
    ) b 
where modf(a.rownum*107+b.rownum*257+17, 101) < <some vaue> 

यह आपको मनमानी पंक्तियों के बीच संयोजन प्राप्त करने देता है।

+0

उत्तर के लिए धन्यवाद; हां, यह यादृच्छिक संयोजन मुझे चाहिए (मैंने स्पष्टता के लिए प्रश्न में समस्याग्रस्त परिणाम जोड़े हैं)। मैंने आपकी दूसरी क्वेरी की कोशिश की, लेकिन मुझे यकीन नहीं है कि 'ओवर' पोस्टग्रेज़ (8.4) द्वारा समर्थित है। क्या यह एक एमएसएसएलएल सर्वर-केवल कीवर्ड है? – halfer

+0

@ हेलफ़र: विंडो फ़ंक्शंस ('row_number()' सहित) पोस्टग्रेज़ 8.4 में समर्थित हैं [http://www.postgresql.org/docs/8.4/interactive/functions-window.html)। हालांकि, 'ओवर (ऑर्डर द्वारा ऑर्डर)' सिर्फ शोर है और इसे 'ओवर() 'में सरलीकृत किया जा सकता है। यादृच्छिक परिणामों के उत्पादन में भी अच्छा नहीं है। आपको एक कार्यान्वयन विशिष्ट, मनमाने ढंग से आदेश मिलता है, ज्यादातर पंक्तियों में प्रवेश के समान ही अनुक्रम में। –

+0

@ErwinBrandstetter - इसके लिए धन्यवाद। मैंने पूरी तरह से 'postgresql over' के लिए खोज की, लेकिन इसे याद किया होगा - शायद 'ओवर' एक शब्द बहुत आम है!मैं इस काम के सेट से परिचित नहीं हूं, इसलिए मैं उन पर पढ़ूंगा। – halfer

3
WITH a_ttl AS (
    SELECT count(*) AS ttl FROM custassets c), 
b_ttl AS (
    SELECT count(*) AS ttl FROM tags), 
rows AS (
    SELECT gs.* 
     FROM generate_series(1, 
      (SELECT max(ttl) AS ttl FROM 
       (SELECT ttl FROM a_ttl UNION SELECT ttl FROM b_ttl) AS m)) 
      AS gs(row)), 
tab_a_rand AS (
    SELECT custassets_id, row_number() OVER (order by random()) as row 
     FROM custassets), 
tab_b_rand AS (
    SELECT id, row_number() OVER (order by random()) as row 
     FROM tags) 
SELECT a.custassets_id, b.id 
    FROM rows r 
    JOIN a_ttl ON 1=1 JOIN b_ttl ON 1=1 
    LEFT JOIN tab_a_rand a ON a.row = (r.row % a_ttl.ttl)+1 
    LEFT JOIN tab_b_rand b ON b.row = (r.row % b_ttl.ttl)+1 
ORDER BY 1,2; 

आप इस क्वेरी का परीक्षण SQL Fiddle पर कर सकते हैं।

+0

पुhew, अगर इरविन के समाधान ने मेरा दिमाग गर्म कर दिया, तो यह एक ब्लैक होल में गिर गया है! एक SQLfiddle के साथ भी महान प्रयास; धन्यवाद और +1। – halfer

1

यादृच्छिक() पर बस एक सादा कार्टेशियन उत्पाद उचित रूप से अच्छी तरह से काम करता प्रतीत होता है। सरल comme Bonjour ...

-- Cartesian product 
-- EXPLAIN ANALYZE 
INSERT INTO dirgraph(point_from,point_to,costs) 
SELECT p1.the_point , p2.the_point, (1000*random()) +1 
FROM allpoints p1 
JOIN allpoints p2 ON random() < 0.002 
     ; 
2

यहाँ एक अलग दृष्टिकोण, यादृच्छिक द्वारा 2 टेबल से एक भी संयोजन लेने के लिए प्राथमिक कुंजी id साथ दो तालिकाओं a और b, दोनों यह मानते हुए है। तालिकाओं को एक ही आकार की आवश्यकता नहीं है, और दूसरी पंक्ति स्वतंत्र रूप से पहले से चुनी जाती है, जो टेस्टडाटा के लिए महत्वपूर्ण नहीं हो सकती है। तुरंत:

SELECT * FROM a, b 
WHERE a.id = (
    SELECT id 
    FROM a 
    OFFSET (
     SELECT random() * (SELECT count(*) FROM a) 
    ) 
    LIMIT 1) 
AND b.id = (
    SELECT id 
    FROM b 
    OFFSET (
     SELECT random() * (SELECT count(*) FROM b) 
     ) 
    LIMIT 1); 

दो तालिकाओं, आकार 7000 पंक्तियों में से एक, 100k पंक्तियों के साथ एक, परिणाम के साथ परीक्षण किया गया। एक से अधिक परिणाम के लिए, आपको बार-बार क्वेरी को कॉल करना होगा - LIMIT में वृद्धि करना और x.id = से x.id IN बदलना होगा (एए, एबी, बीए, बीबी) परिणाम पैटर्न।

+0

एक बहुत उपन्यास समाधान, अच्छी चीजें। धन्यवाद! – halfer