SQL सर्वर तालिका से n यादृच्छिक पंक्तियों का चयन करें


309

मुझे इसमें लगभग 50,000 पंक्तियों के साथ एक SQL सर्वर तालिका मिली है। मैं यादृच्छिक रूप से उन पंक्तियों में से लगभग 5,000 का चयन करना चाहता हूं। मैंने एक जटिल तरीके से सोचा है, एक "यादृच्छिक संख्या" कॉलम के साथ एक अस्थायी तालिका बना रहा है, मेरी तालिका को उस में कॉपी कर रहा है, अस्थायी तालिका के माध्यम से लूपिंग और प्रत्येक पंक्ति को अपडेट कर रहा है RAND(), और फिर उस तालिका से चयन कर रहा है जहां यादृच्छिक संख्या स्तंभ < 0.1। यदि संभव हो तो एक भी बयान में, मैं इसे करने का एक सरल तरीका ढूंढ रहा हूं।

यह आलेखNEWID() फ़ंक्शन का उपयोग करने का सुझाव देता है। यह आशाजनक लग रहा है, लेकिन मैं यह नहीं देख सकता कि मैं निश्चित रूप से कुछ प्रतिशत पंक्तियों का चयन कैसे कर सकता हूं।

पहले कभी कोई ऐसा करता है? कोई विचार?


3
MSDN का एक अच्छा लेख है जो इन मुद्दों का एक बहुत कुछ शामिल करता है: बड़ी तालिका से
रैंड्स को रैंडम तरीके

जवाबों:


387
select top 10 percent * from [yourtable] order by newid()

बड़ी तालिकाओं के विषय में "शुद्ध कचरा" टिप्पणी के जवाब में: आप प्रदर्शन को बेहतर बनाने के लिए ऐसा कर सकते हैं।

select  * from [yourtable] where [yourPk] in 
(select top 10 percent [yourPk] from [yourtable] order by newid())

इसकी लागत मूल्यों की महत्वपूर्ण स्कैन होगी और साथ ही लागत भी शामिल होगी, जो कि एक छोटे से चयन के साथ एक बड़ी मेज पर उचित होनी चाहिए।


1
मुझे यह दृष्टिकोण बहुत अच्छा लगा, फिर उन्होंने जिस लेख का हवाला दिया।
जोशबर्के

14
यह हमेशा ध्यान में रखना अच्छा है कि न्यूड () एक बहुत अच्छा छद्म आयामी संख्या जनरेटर नहीं है, कम से कम लगभग रैंड जितना अच्छा नहीं है ()। लेकिन अगर आपको कुछ अस्पष्ट बेतरतीब नमूनों की ज़रूरत है और गणितीय गुणों और इस तरह की परवाह नहीं करते हैं, तो यह काफी अच्छा होगा। अन्यथा आपको आवश्यकता है: stackoverflow.com/questions/249301/…
user12861

1
उम, माफ करना अगर यह स्पष्ट है .. लेकिन क्या [yourPk]संदर्भित करता है? संपादित करें: Nvm, यह समझ से बाहर ... प्राथमिक कुंजी। दुर्र
स्नीलर

4
newid - गाइड को अद्वितीय होने के लिए निरुपित किया गया है लेकिन यादृच्छिक नहीं है। गलत दृष्टिकोण
Brans Ds

2
बड़ी संख्या में पंक्तियों के साथ उदाहरण के लिए 1 मिलियन से अधिक newid()क्रमबद्ध अनुमान I / O लागत बहुत अधिक होगी और प्रदर्शन को प्रभावित करेगा।
अनादि १२

81

आपकी आवश्यकताओं के आधार पर, TABLESAMPLEआपको लगभग यादृच्छिक और बेहतर प्रदर्शन मिलेगा। यह MS SQL सर्वर 2005 और बाद में उपलब्ध है।

TABLESAMPLE यादृच्छिक पंक्तियों के बजाय यादृच्छिक पृष्ठों से डेटा लौटाएगा और इसलिए डेटा को पुनर्प्राप्त भी नहीं करेगा कि यह वापस नहीं आएगा।

एक बहुत बड़ी मेज पर मैंने परीक्षण किया

select top 1 percent * from [tablename] order by newid()

20 मिनट से अधिक समय लगा।

select * from [tablename] tablesample(1 percent)

2 मिनट लगे।

प्रदर्शन छोटे नमूनों पर भी सुधरेगा, TABLESAMPLEजबकि ऐसा नहीं होगा newid()

कृपया ध्यान रखें कि यह newid()विधि के रूप में यादृच्छिक नहीं है, लेकिन आपको एक सभ्य नमूना देगा।

MSDN पृष्ठ देखें ।


7
जैसा कि नीचे Rob Boek द्वारा बताया गया है, टेबल क्लैंपिंग के परिणाम सामने आते हैं, और इसलिए यह यादृच्छिक परिणामों की एक छोटी संख्या प्राप्त करने का एक अच्छा तरीका नहीं है
Oskar Austegard

आप यह सवाल उठाते हैं कि यह कैसे काम करता है: newid () से newid () से शीर्ष 1 प्रतिशत * का चयन करें क्योंकि newid () [tablename] में कोई कॉलम नहीं है। क्या प्रत्येक पंक्ति में आंतरिक रूप से कॉलम न्यूड () जोड़कर sql सर्वर है और फिर एक प्रकार है?
FrenkyB

जब मैं एक बहुत बड़ी टेबल पर एक जटिल प्रश्न कर रहा था, तो मेरे लिए सबसे अच्छा जवाब था। कोई सवाल नहीं है कि यह उल्लेखनीय रूप से तेज था। मुझे कई बार भागे गए संख्या रिकॉर्ड में भिन्नता मिली, लेकिन उनमें से सभी त्रुटि के एक स्वीकार्य मार्जिन के भीतर थे।
jessier3

38

newid () / आदेश द्वारा काम करेगा, लेकिन बड़े परिणाम सेटों के लिए बहुत महंगा होगा क्योंकि इसे हर पंक्ति के लिए एक आईडी उत्पन्न करना होगा, और फिर उन्हें क्रमबद्ध करना होगा।

TABLESAMPLE () एक प्रदर्शन के दृष्टिकोण से अच्छा है, लेकिन आपको परिणामों की गड़गड़ाहट मिलेगी (एक पृष्ठ पर सभी पंक्तियों को वापस कर दिया जाएगा)।

एक बेहतर प्रदर्शन करने वाले सच्चे यादृच्छिक नमूने के लिए, सबसे अच्छा तरीका है कि बेतरतीब ढंग से पंक्तियों को फ़िल्टर किया जाए। मुझे निम्न कोड नमूना SQL सर्वर पुस्तकें ऑनलाइन लेख सीमा परिणाम सेट में TABLESAMPLE का उपयोग करके मिला :

यदि आप वास्तव में व्यक्तिगत पंक्तियों का एक यादृच्छिक नमूना चाहते हैं, तो TABLESAMPLE का उपयोग करने के बजाय, पंक्तियों को यादृच्छिक रूप से फ़िल्टर करने के लिए अपनी क्वेरी को संशोधित करें। उदाहरण के लिए, निम्न क्वेरी बिक्री के लगभग एक प्रतिशत पंक्तियों को वापस करने के लिए NEWID फ़ंक्शन का उपयोग करती है। SalesOrderDetail Table:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

SalesOrderID कॉलम CHECKSUM अभिव्यक्ति में शामिल है ताकि NEWID () प्रति पंक्ति के आधार पर नमूना प्राप्त करने के लिए प्रति पंक्ति एक बार मूल्यांकन करे। अभिव्यक्ति CAST (CHECKSUM (NEWID), SalesOrderID) और 0x7fffffff AS फ्लोट / CAST (0x7fffffff AS int) 0 और 1 के बीच एक यादृच्छिक फ्लोट मान का मूल्यांकन करता है।

जब 1,000,000 पंक्तियों वाली तालिका के खिलाफ चलाया जाता है, तो यहां मेरे परिणाम हैं:

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

यदि आप TABLESAMPLE के उपयोग से दूर हो सकते हैं, तो यह आपको सर्वश्रेष्ठ प्रदर्शन देगा। अन्यथा न्यूड () / फिल्टर विधि का उपयोग करें। यदि आपके पास एक बड़ा परिणाम है, तो newid () / ऑर्डर अंतिम उपाय होना चाहिए।


मैंने उस लेख को भी देखा और अपने कोड पर इसे आज़माते हुए, ऐसा लगता है कि NewID()प्रति पंक्ति के बजाय केवल एक बार मूल्यांकन किया जाता है, जो मुझे पसंद नहीं है ...
एंड्रयू माओ

23

MSDN पर एक बड़ी तालिका से बेतरतीब ढंग से पंक्तियों का चयन करना एक सरल, अच्छी तरह से स्पष्ट समाधान है जो बड़े पैमाने पर प्रदर्शन चिंताओं को संबोधित करता है।

  SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10

बहुत ही रोचक। लेख को पढ़ने के बाद, मुझे वास्तव में समझ में नहीं आया कि RAND()प्रत्येक पंक्ति के लिए समान मान क्यों नहीं लौटाता (जो BINARY_CHECKSUM()तर्क को हरा देगा )। क्या यह इसलिए है क्योंकि इसे सेलेक्ट क्लॉज का हिस्सा होने के बजाय दूसरे फंक्शन के अंदर बुलाया जा रहा है?
जॉन एम गैंट

यह क्वेरी 6MM पंक्तियों वाली तालिका में एक सेकंड से भी कम समय में चली।
मार्क मेलविल

2
मैंने इस प्रविष्टि को 35 प्रविष्टियों के साथ एक मेज पर चलाया है और परिणाम सेट में उनमें से दो को बहुत बार रखा है। यह rand()उपरोक्त के साथ या संयोजन में एक समस्या हो सकती है - लेकिन मैं इस कारण से इस समाधान से दूर हो गया। इसके अलावा परिणामों की संख्या 1 से 5 तक भिन्न है, इसलिए यह कुछ परिदृश्यों में स्वीकार्य नहीं भी हो सकता है।
ओलिवर

क्या प्रत्येक पंक्ति के लिए RAND () समान मान नहीं लौटाता है?
सरसापरिला

RAND()प्रत्येक पंक्ति के लिए समान मान लौटाता है (यही कारण है कि यह समाधान तेज है)। हालांकि, बाइनरी चेकसम के साथ पंक्तियाँ जो एक साथ बहुत करीब होती हैं, समान चेकसम परिणाम उत्पन्न करने के उच्च जोखिम में होती हैं, जिससे RAND()छोटी होने पर अकड़न होती है। जैसे, (ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100== SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100। यदि आपका डेटा इस समस्या से ग्रस्त है, तो BINARY_CHECKSUM9923 से गुणा करें।
ब्रायन

12

इस लिंक में ऑर्डरबी (NEWID ()) और 1, 7, और 13 लाखों पंक्तियों वाली तालिकाओं के लिए अन्य तरीकों के बीच एक दिलचस्प तुलना है।

अक्सर, जब चर्चा समूहों में यादृच्छिक पंक्तियों का चयन करने का तरीका पूछा जाता है, तो NEWID क्वेरी प्रस्तावित होती है; यह सरल है और छोटे तालिकाओं के लिए बहुत अच्छी तरह से काम करता है।

SELECT TOP 10 PERCENT *
  FROM Table1
  ORDER BY NEWID()

हालाँकि, बड़ी तालिकाओं के लिए उपयोग करते समय NEWID क्वेरी में एक बड़ी खामी होती है। ORDER BY क्लॉज़ तालिका में सभी पंक्तियों को tempdb डेटाबेस में कॉपी करने का कारण बनता है, जहाँ उन्हें क्रमबद्ध किया जाता है। यह दो समस्याओं का कारण बनता है:

  1. सॉर्टिंग ऑपरेशन में आमतौर पर एक उच्च लागत होती है। सॉर्टिंग डिस्क I / O का बहुत उपयोग कर सकती है और लंबे समय तक चल सकती है।
  2. सबसे खराब स्थिति में, अस्थायी स्पेस से बाहर चला जा सकता है। सबसे अच्छी स्थिति में, tempdb एक बड़ी मात्रा में डिस्क स्थान ले सकता है जिसे मैन्युअल रूप से सिकुड़े कमांड के बिना कभी भी पुनः प्राप्त नहीं किया जाएगा।

आपको जिस चीज की जरूरत है, वह पंक्तियों को बेतरतीब ढंग से चुनने का एक तरीका है जो टेम्पर्ड बी का उपयोग नहीं करेगा और टेबल जितना बड़ा हो जाएगा उतना धीमा नहीं होगा। यहाँ कैसे करना है पर एक नया विचार है:

SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10

इस क्वेरी के पीछे मूल विचार यह है कि हम तालिका में प्रत्येक पंक्ति के लिए 0 और 99 के बीच एक यादृच्छिक संख्या उत्पन्न करना चाहते हैं, और फिर उन सभी पंक्तियों को चुनें जिनकी यादृच्छिक संख्या निर्दिष्ट प्रतिशत के मान से कम है। इस उदाहरण में, हम लगभग 10 प्रतिशत पंक्तियों को यादृच्छिक रूप से चयनित करना चाहते हैं; इसलिए, हम उन सभी पंक्तियों को चुनते हैं जिनकी यादृच्छिक संख्या 10 से कम है।

कृपया MSDN में पूरा लेख पढ़ें ।


2
हाय डम्बर, अच्छा पाया, आप इसे लिंक से हटा सकते हैं क्योंकि केवल उत्तर ही नष्ट होने की संभावना है।
बुम्मी

1
@ बम्मी मैंने इसे केवल लिंक से बचने के लिए बदल दिया :)
QMaster

यह सबसे अच्छा जवाब है। 'ORDER BY NEWID ()' ज्यादातर मामलों (छोटे तालिकाओं) में काम करता है, लेकिन जैसा कि refrenced लिंक में बेंचमार्क स्पष्ट रूप से यह दिखाता है कि तालिका बढ़ती है
पैडराम बशीरी

10

यदि आपको (ओपी के विपरीत) एक विशिष्ट संख्या में रिकॉर्ड की आवश्यकता होती है (जो चेकस्कैम दृष्टिकोण को कठिन बनाता है) और स्वयं द्वारा प्रदान किए गए TABLESAMPLE की तुलना में अधिक यादृच्छिक नमूना चाहते हैं, और चेकबॉक्स की तुलना में बेहतर गति भी चाहते हैं, तो आप इसे विलय के साथ कर सकते हैं TABLESAMPLE और NEWID () विधियाँ, इस प्रकार हैं:

DECLARE @sampleCount int = 50
SET STATISTICS TIME ON

SELECT TOP (@sampleCount) * 
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()

SET STATISTICS TIME OFF

मेरे मामले में यह यादृच्छिकता (यह वास्तव में नहीं है, मुझे पता है) और गति के बीच सबसे सीधा समझौता है। TABLESAMPLE प्रतिशत (या पंक्तियों) को उचित के रूप में भिन्न करें - उच्चतर प्रतिशत, अधिक यादृच्छिक नमूना, लेकिन गति में रैखिक गिरावट की अपेक्षा करें। (ध्यान दें कि TABLESAMPLE एक चर को स्वीकार नहीं करेगा)


9

बस एक यादृच्छिक संख्या द्वारा तालिका का आदेश दें और पहली 5,000 पंक्तियों का उपयोग करके प्राप्त करें TOP

SELECT TOP 5000 * FROM [Table] ORDER BY newid();

अपडेट करें

बस यह कोशिश की और एक newid()फोन पर्याप्त है - सभी कलाकारों और सभी गणित के लिए कोई ज़रूरत नहीं है।


10
बेहतर प्रदर्शन के लिए 'सभी जातियों और सभी गणितों' का उपयोग किया जाता है।
hkf

6

यह प्रारंभिक बीज विचार और एक चेकसम का संयोजन है, जो मुझे NEWID की लागत के बिना ठीक से यादृच्छिक परिणाम देने के लिए दिखता है ():

SELECT TOP [number] 
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())

3

MySQL में आप ऐसा कर सकते हैं:

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;

3
यह काम नहीं करेगा। चूंकि चयनित कथन परमाणु है, यह केवल एक यादृच्छिक संख्या को पकड़ता है और प्रत्येक पंक्ति के लिए इसे डुप्लिकेट करता है। आपको इसे बदलने के लिए बाध्य करने के लिए प्रत्येक पंक्ति पर इसे फिर से लिखना होगा।
टॉम एच

4
मम्म ... वेंडर मतभेदों से प्यार करते हैं। चयन MySQL पर परमाणु है, लेकिन मुझे एक अलग तरीके से लगता है। यह MySQL में काम करेगा।
जेफ फेरलैंड

2

अभी तक उत्तरों में यह भिन्नता नहीं देखी। मेरे पास एक अतिरिक्त अड़चन थी जहां मुझे जरूरत थी, एक प्रारंभिक बीज दिया, हर बार पंक्तियों के समान सेट का चयन करने के लिए।

MS SQL के लिए:

न्यूनतम उदाहरण:

select top 10 percent *
from table_name
order by rand(checksum(*))

सामान्यीकृत निष्पादन समय: 1.00

NewId () उदाहरण:

select top 10 percent *
from table_name
order by newid()

सामान्यीकृत निष्पादन समय: 1.02

NewId()तुच्छ रूप से धीमा है rand(checksum(*)), इसलिए आप इसे बड़े रिकॉर्ड सेट के खिलाफ उपयोग नहीं करना चाहते हैं।

प्रारंभिक बीज के साथ चयन:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % @seed) /* any other math function here */

यदि आपको बीज दिए गए एक ही सेट का चयन करने की आवश्यकता है, तो यह काम करने लगता है।


क्या RAND () के खिलाफ विशेष @seed का उपयोग करने का कोई फायदा है?
QMaster

बिल्कुल, आपने बीज पैरामीटर का उपयोग किया और इसे दिनांक पैरामीटर से भर दिया, RAND () फ़ंक्शन पूरे समय के मूल्य का उपयोग करने के अलावा करता है, मैं जानना चाहता हूं कि RAND () या नहीं के ऊपर बीज जैसे काम के लिए बनाया पैरामीटर का उपयोग करने का कोई फायदा है या नहीं?
क्यूमास्टर

आह !. ठीक है, यह परियोजना की एक आवश्यकता थी। मुझे नियतात्मक तरीके से n-random पंक्तियों की सूची तैयार करने की आवश्यकता थी। मूल रूप से नेतृत्व यह जानना चाहता था कि "यादृच्छिक" पंक्तियों को हम क्या चुनते हैं जब पंक्तियों को चुना और संसाधित किया जाता है। वर्ष / माह के आधार पर बीज मूल्य का निर्माण करके मैं उस प्रश्न के लिए किसी भी कॉल की गारंटी दे सकता हूं जो उसी वर्ष "रैंडम" सूची को लौटा देगा। मुझे पता है, यह अजीब था और शायद बेहतर तरीके थे लेकिन इसने काम किया ...
klyd

हाहा :) मैं देखता हूं, लेकिन मुझे लगता है कि यादृच्छिक चयनित रिकॉर्ड का सामान्य अर्थ अलग-अलग चल रहे क्वेरी पर समान रिकॉर्ड नहीं है।
क्यूमास्टर


0

यह न्यूड दिखाई देता है (जहां क्लॉज़ का उपयोग नहीं किया जा सकता है, इसलिए इस समाधान के लिए एक आंतरिक क्वेरी की आवश्यकता होती है:

SELECT *
FROM (
    SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
    FROM MyTable
) vw
WHERE Rnd % 100 < 10        --10%

0

मैं इसे सबक्वेरी में इस्तेमाल कर रहा था और इसने मुझे सबक्वेरी में वही पंक्तियाँ लौटा दीं

 SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

तो मैं कहाँ में मूल तालिका चर सहित के साथ हल

SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              Where Mytable.ID>0
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

जहाँ संक्षेपण नोट करें


0

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

ORDER BY RAND () में कई रिकॉर्ड शामिल हैं, इस पर निर्भर करते हुए, प्रदर्शन पर जुर्माना लग सकता है।


मुझे ठीक से याद नहीं है कि मैं उस समय क्या उपयोग कर रहा था, लेकिन मैं शायद C # में काम कर रहा था, शायद सर्वर पर, या शायद क्लाइंट एप्लिकेशन में, निश्चित नहीं। C # के पास PHP के फेरबदल की तुलना में सीधे तौर पर कुछ भी नहीं है, लेकिन यह एक चुनिंदा ऑपरेशन के भीतर रैंडम ऑब्जेक्ट से फ़ंक्शन को लागू करके किया जा सकता है, परिणाम का आदेश दे सकता है, और फिर शीर्ष दस प्रतिशत ले सकता है। लेकिन हमें डीबी सर्वर पर डिस्क से पूरी तालिका को पढ़ना होगा और इसे नेटवर्क पर प्रसारित करना होगा, केवल उस डेटा का 90% त्यागना होगा। इसे सीधे DB में संसाधित करना लगभग निश्चित रूप से अधिक कुशल है।
जॉन एम गेंट

-2

यह मेरे लिए काम करता है:

SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]

9
@ user537824, क्या आपने SQL सर्वर पर कोशिश की थी? रैंडम एक फ़ंक्शन नहीं है और लिमिट एक कीवर्ड नहीं है। SQL सर्वर सिंटैक्स जो आप कर रहे हैं वह होगा select top 10 percent from table_name order by rand(), लेकिन वह भी काम नहीं करता है क्योंकि सभी पंक्तियों पर रैंड () एक ही मान देता है।
जॉन एम गैंट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.