मैं एक चयन कथन में प्रत्येक पंक्ति के लिए अलग-अलग यादृच्छिक मान कैसे निर्दिष्ट कर सकता हूं?


11

कृपया इस कोड को देखें:

create table #t1(
  id int identity (1,1),
  val varchar(10)
);


insert into #t1 values ('a');
insert into #t1 values ('b');
insert into #t1 values ('c');
insert into #t1 values ('d');

अब, जब भी आप इस पर अमल करते हैं

select *, 
    ( select top 1 val from #t1 order by NEWID()) rnd 
from #t1 order by 1;

आपको एक परिणाम मिलेगा जहां सभी पंक्तियों का समान यादृच्छिक मूल्य है। जैसे

id          val        rnd
----------- ---------- ----------
1           a          b
2           b          b
3           c          b
4           d          b

मुझे पता है कि एक कर्सर का उपयोग करके लूप को पंक्तियों को फेंक दिया जाता है और विभिन्न यादृच्छिक मान प्राप्त किए जाते हैं, लेकिन यह प्रदर्शन योग्य नहीं है।

इसका एक चतुर समाधान है

select t1.id, t1.val, t2.val
from #t1 t1
    join (select *, ROW_NUMBER() over( order by NEWID()) lfd from #t1) as t2 on  t1.id = t2.lfd 

लेकिन मैंने क्वेरी को सरल बनाया। वास्तविक क्वेरी अधिक दिखती है

select *, 
    ( select top 1 val from t2 where t2.x <> t1.y order by NEWID()) rnd 
from t1 order by 1;

और सरल समाधान फिट नहीं है। मैं बार-बार मूल्यांकन के लिए मजबूर करने के लिए रास्ता तलाश रहा हूं

( select top 1 val from #t1 order by NEWID()) rnd 

बिना अभिशापों के उपयोग के।

संपादित करें: उत्पादन चाहता था:

शायद 1 कॉल

id          val        rnd
----------- ---------- ----------
1           a          c
2           b          c
3           c          b
4           d          a

और एक दूसरी कॉल

id          val        rnd
----------- ---------- ----------
1           a          a
2           b          d
3           c          d
4           d          b

प्रत्येक पंक्ति के लिए मान अन्य पंक्तियों से स्वतंत्र एक यादृच्छिक मान होना चाहिए

यहाँ कोड का कर्सर संस्करण है:

CREATE TABLE #res ( id INT, val VARCHAR(10), rnd VARCHAR(10));

DECLARE @id INT
DECLARE @val VARCHAR(10)
DECLARE c CURSOR FOR
SELECT id, val
FROM #t1
OPEN c
FETCH NEXT FROM c INTO @id, @val
WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO #res
    SELECT @id, @val, ( SELECT TOP 1 val FROM #t1 ORDER BY NEWID()) rnd 
    FETCH NEXT FROM c INTO @id, @val
END
CLOSE c
DEALLOCATE c

SELECT * FROM #res

कृपया आपका सही आउटपुट क्या होगा? शायद मुझे कुछ याद आ रहा है
gbn

मैं यह स्पष्ट करने के लिए एक कर्सर संस्करण तैयार कर रहा हूं
bnd_k

तो रैंड और वैल हर पंक्ति में हमेशा अलग होते हैं? यदि यह "यादृच्छिक" होता, तो कभी-कभी वे समान होते। इसके अलावा, आपके द्वारा बताई गई 2 कॉलों में यह मायने रखता है कि rnd में कॉलम पर सभी मान नहीं हैं?
gbn

इसका उपयोग वास्तविक डेटा के एक बड़े पूल से एक छोटे से मध्यम यादृच्छिक प्रदर्शन को उत्पन्न करने के लिए किया जाता है। हां रिप्लेसमेंट की अनुमति है।
bernd_k

जवाबों:


11

यदि संभव हो तो एक बार एक उपश्रेणी का मूल्यांकन किया जाता है। मुझे याद नहीं है कि "फ़ीचर" को क्या कहा जाता है (तह?) क्षमा करें।

यही बात GETDATE और RAND फ़ंक्शन पर लागू होती है। NEWID का मूल्यांकन पंक्ति द्वारा किया जाता है क्योंकि यह आंतरिक रूप से एक यादृच्छिक मूल्य होता है और कभी भी एक ही मूल्य को दो बार उत्पन्न नहीं करना चाहिए।

सामान्य तकनीकों का उपयोग NEWID का उपयोग CHECKSUM के इनपुट के रूप में या रैंड के बीज के रूप में किया जाता है

पंक्ति प्रति यादृच्छिक मानों के लिए:

SELECT
   co1l, col2,
   ABS(CHECKSUM(NEWID())) AS Random1,
   RAND(CHECKSUM(NEWID())) AS Random2
FROM
   MyTable

यदि आप यादृच्छिक क्रम चाहते हैं:

SELECT
   co1l, col2
FROM
   MyTable
ORDER BY
   NEWID()

यदि आप पंक्ति क्रम के साथ यादृच्छिक क्रम भी चाहते हैं। परिणामी के आदेश की परवाह किए बिना यहां वास्तविक आदेश को संरक्षित किया जाता है

SELECT
   id, val,
   ROWNUMBER() OVER (ORDER BY id) AS id
FROM
   #t1
ORDER BY
   NEWID()

संपादित करें:

इस मामले में, हम आवश्यकता के रूप में बता सकते हैं:

  1. सेट में प्रत्येक पंक्ति के लिए सेट से कोई भी यादृच्छिक मान लौटाएं
  2. यादृच्छिक मूल्य किसी भी पंक्ति में वास्तविक मूल्य से अलग होगा

यह जो मैंने ऊपर की पेशकश की है, वह विभिन्न तरीकों से पंक्तियों को फिर से क्रमबद्ध करने के लिए अलग है

इसलिए, मैं CROSS APPLY पर विचार करूंगा। WHERE क्लॉज़ फ़ोर्स रो को पंक्ति मूल्यांकन द्वारा और "फोल्डिंग" समस्या से बचा जाता है और यह सुनिश्चित करता है कि वैल और रैंड हमेशा अलग होते हैं। CROSS APPLY काफी अच्छी तरह से स्केल कर सकती है

SELECT
   id, val, R.rnd
FROM
   #t1 t1
   CROSS APPLY
   (SELECT TOP 1 val as rnd FROM #t1 t2 WHERE t1.val <> t2.val ORDER BY NEWID()) R
ORDER BY
   id

APPLY SQL Server 2005 और ऊपरी
bernd_k

1
@bernd_k: हाँ, लेकिन यह 2011 में SQL Server 2000 उपयोगकर्ताओं को अनदेखा करने के लिए यथार्थवादी होना चाहिए ...
G12
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.