Oracle 11.2.0.4.0 पर, जब मैं निम्नलिखित क्वेरी चलाता हूं तो प्रत्येक पंक्ति को एक अलग परिणाम मिलता है:

select r.n from (
  select trunc(dbms_random.value(1, 100)) n from dual
) r
connect by level < 100; -- returns random values

लेकिन जैसे ही मैं प्राप्त यादृच्छिक मान को एक जॉइन या सबक्वेरी में उपयोग करता हूं तो प्रत्येक पंक्ति को dbms_random.value से समान मान मिलता है:

select r.n, (select r.n from dual) from (
  select trunc(dbms_random.value(1, 100)) n from dual
) r
connect by level < 100; -- returns the same value each time

क्या दूसरी क्वेरी को प्रत्येक पंक्ति के लिए यादृच्छिक मान वापस करना संभव है?

अपडेट करें

मेरा उदाहरण शायद अति-सरलीकृत था, यहाँ मैं क्या करने की कोशिश कर रहा हूँ:

with reservations(val) as (
  select 1 from dual union all
  select 3 from dual union all
  select 4 from dual union all
  select 5 from dual union all
  select 8 from dual
)
select * from (
  select rnd.val, CONNECT_BY_ISLEAF leaf from (
    select trunc(dbms_random.value(1, 10)) val from dual
  ) rnd
  left outer join reservations res on res.val = rnd.val
  connect by res.val is not null
)
where leaf = 1;

लेकिन आरक्षण के साथ जो 1 से 1.000.000.000 (और अधिक) तक जा सकता है। कभी-कभी वह क्वेरी सही ढंग से वापस आती है (यदि उसने तुरंत एक यादृच्छिक मान चुना जिसके लिए कोई आरक्षण नहीं था) या स्मृति त्रुटि देता है क्योंकि यह हमेशा dbms_random.value के समान मान के साथ प्रयास करता है।

3
Xavier Dury 10 जुलाई 2018, 11:46

3 जवाब

सबसे बढ़िया उत्तर

आपकी टिप्पणी "... और मैं समवर्ती समस्याओं से बचना चाहता हूं" ने मुझे सोचने पर मजबूर कर दिया।

आप केवल एक यादृच्छिक संख्या डालने का प्रयास क्यों नहीं करते, डुप्लिकेट उल्लंघनों से सावधान रहें, और सफल होने तक पुनः प्रयास करें? यहां तक ​​​​कि एक बहुत ही चतुर समाधान जो उपलब्ध संख्याओं को देखता है, दो अलग-अलग सत्रों में समान नए नंबरों के साथ आ सकता है। तो, केवल एक सम्मिलित और प्रतिबद्ध आरक्षण संख्या सुरक्षित है।

1
wolφi 10 जुलाई 2018, 15:44

आप सबक्वायरी के अंदर कनेक्ट-बाय क्लॉज को स्थानांतरित कर सकते हैं:

select r.n, (select r.n from dual) from (
  select trunc(dbms_random.value(1, 100)) n from dual
  connect by level < 100
) r;

         N (SELECTR.NFROMDUAL)
---------- -------------------
        90                  90
        69                  69
        15                  15
        53                  53
         8                   8
         3                   3
...

मैं जो करने की कोशिश करता हूं वह यादृच्छिक संख्याओं का अनुक्रम उत्पन्न करता है और पहले वाला ढूंढता है जिसके लिए मेरे पास किसी तालिका में रिकॉर्ड नहीं है

आप संभावित रूप से कुछ ऐसा कर सकते हैं:

select r.n
from (
  select trunc(dbms_random.value(1, 100)) n from dual
  connect by level < 100
) r
where not exists (
  select id from your_table where id = r.n
)
and rownum = 1;

लेकिन यह उनमें से किसी की भी जाँच करने से पहले सभी 100 यादृच्छिक मान उत्पन्न करेगा, जो थोड़ा बेकार है; और जैसा कि आपको उन 100 में अंतर नहीं मिल सकता है (और उन सौ के भीतर डुप्लिकेट हो सकते हैं) आपको या तो बहुत बड़ी रेंज की आवश्यकता है जो कि महंगा भी है, हालांकि इतने सारे यादृच्छिक कॉल होने की आवश्यकता नहीं है:

select min(r.n) over (order by dbms_random.value) as n
from (
  select level n from dual
  connect by level < 100 -- or entire range of possible values
) r
where not exists (
  select id from your_table where id = r.n
)
and rownum = 1;

या एक मैच मिलने तक एक ही चेक दोहराएं।

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

और यदि आपके पास अपेक्षाकृत कम संख्या में अंतराल हैं (और आप वास्तव में उनको पुन: उपयोग करना चाहते हैं) तो आप संभवतः केवल निर्दिष्ट सीमा के भीतर एक अंतर की खोज कर सकते हैं और फिर कोई अंतराल नहीं होने पर सीक्वेंसर पर वापस आ सकते हैं। मान लें कि आपके पास वर्तमान में उपयोग किए जाने वाले 1 से 1000 के बीच के मान हैं, जिनमें से कुछ गायब हैं; आप उस 1-100 रेंज में एक मुफ्त मूल्य की तलाश कर सकते हैं, और यदि कोई नहीं हैं तो इसके बजाय 1001 प्राप्त करने के लिए अनुक्रम का उपयोग करें, बजाय इसके कि आप हमेशा अपने अंतराल खोज में मूल्यों की पूरी संभावित सीमा शामिल करें। यह उपयोग की गई सीमा को बढ़ाने के लिए वरीयता में अंतराल को भी भरेगा, जो उपयोगी हो भी सकता है और नहीं भी। (मुझे यकीन नहीं है कि "मुझे लगातार होने के लिए उन नंबरों की आवश्यकता नहीं है" का अर्थ है कि उन्हें लगातार नहीं होना चाहिए, या इससे कोई फर्क नहीं पड़ता)।

जब तक आपके पास विशेष रूप से किसी व्यवसाय को अंतराल को भरने की आवश्यकता नहीं होती है और असाइन किए गए मान लगातार नहीं होते हैं, हालांकि, मैं केवल एक अनुक्रम का उपयोग करता हूं और अंतराल को अनदेखा करता हूं।

1
Alex Poole 10 जुलाई 2018, 13:29

मैं निम्नलिखित क्वेरी के साथ एक सही परिणाम प्राप्त करने में कामयाब रहा लेकिन मुझे यकीन नहीं है कि यह दृष्टिकोण वास्तव में उचित है या नहीं:

with
  reservations(val) as (
    select 1 from dual union all
    select 3 from dual union all
    select 4 from dual union all
    select 5 from dual union all
    select 8 from dual
  ),
  rand(v) as (
    select trunc(dbms_random.value(1, 10)) from dual
  ),
  next_res(v, ok) as (
    select v, case when exists (select 1 from reservations r where r.val = rand.v) then 0 else 1 end from rand
  ),
  recursive(i, v, ok) AS (
    select 0, 0, 0 from dual
    union all
    select i + 1, next_res.v, next_res.ok from recursive, next_res where i < 100 /*maxtries*/ and recursive.ok = 0
)
select v from recursive where ok = 1;
1
Xavier Dury 10 जुलाई 2018, 14:38