यादृच्छिक क्रम प्राप्त करने का सबसे अच्छा तरीका क्या है?


27

मेरे पास एक क्वेरी है जहां मैं चाहता हूं कि परिणामी रिकॉर्ड यादृच्छिक रूप से ऑर्डर किए जाएं। यह एक संकुल सूचकांक का उपयोग करता है, इसलिए यदि मैं order byइसे शामिल नहीं करता हूं तो यह संभवत: उस सूचकांक के क्रम में रिटर्न रिकॉर्ड करेगा। मैं एक यादृच्छिक पंक्ति क्रम कैसे सुनिश्चित कर सकता हूं?

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

जवाबों:


19

NEWID द्वारा आदेश () रिकॉर्ड्स को यादृच्छिक रूप से सॉर्ट करेगा। यहाँ एक उदाहरण है

SELECT *
FROM Northwind..Orders 
ORDER BY NEWID()

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

4
प्रदर्शन के दृष्टिकोण से, यह काफी धीमा है - आप ORDER BY CHECKSUM (NEWID ()) द्वारा एक महत्वपूर्ण सुधार प्राप्त कर सकते हैं
Miles D

1
@ मर्डनी - आप "सांख्यिकीय रूप से यादृच्छिक नहीं" को क्या आधार देते हैं? यहाँ उत्तर कहता है कि यह CryptGenRandomअंत में उपयोग करके समाप्त होता है । dba.stackexchange.com/a/208069/3690
मार्टिन स्मिथ

15

प्रदीप अडिगा का पहला सुझाव, ORDER BY NEWID()ठीक है और कुछ इस कारण से मैंने अतीत में उपयोग किया है।

उपयोग करने में सावधानी बरतें RAND()- कई संदर्भों में यह केवल एक बार कथन के अनुसार निष्पादित किया जाता है, इसलिए ORDER BY RAND()इसका कोई प्रभाव नहीं होगा (जैसा कि आप प्रत्येक पंक्ति के लिए RAND () से एक ही परिणाम प्राप्त कर रहे हैं)।

उदाहरण के लिए:

SELECT display_name, RAND() FROM tr_person

हमारी व्यक्ति तालिका से एक नाम और एक "यादृच्छिक" संख्या देता है, जो प्रत्येक पंक्ति के लिए समान है। प्रत्येक बार जब आप क्वेरी चलाते हैं, तो संख्या भिन्न होती है, लेकिन हर बार प्रत्येक पंक्ति के लिए समान होती है।

यह दिखाने के लिए कि RAND()एक ORDER BYक्लॉज में इस्तेमाल किया गया मामला ऐसा ही है , मैं कोशिश करता हूं:

SELECT display_name FROM tr_person ORDER BY RAND(), display_name

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

NEWID()हालांकि काम करना आदेश है , क्योंकि यदि NEWID () को हमेशा यूयूआईडी के उद्देश्य को आश्वस्त नहीं किया जाता है, तो एक ही राज्य में कई नई पंक्तियों को अद्वितीय पहचानकर्ताओं के साथ सम्मिलित करते हुए तोड़ा जाएगा, क्योंकि वे कुंजी हैं:

SELECT display_name FROM tr_person ORDER BY NEWID()

है नाम "बेतरतीब ढंग से" आदेश।

अन्य DBMS

उपरोक्त MSSQL के लिए सही है (2005 और 2008 कम से कम, और अगर मुझे सही रूप में 2000 भी याद है)। एक नया UUID लौटाने वाले फ़ंक्शन का मूल्यांकन हर बार सभी DBMSs NEWID () MSSQL के अंतर्गत किया जाना चाहिए, लेकिन यह प्रलेखन और / या अपने स्वयं के परीक्षणों द्वारा इसे सत्यापित करने के लायक है। अन्य मनमाने परिणाम वाले कार्यों का व्यवहार, जैसे RAND (), DBMSs के बीच भिन्न होने की अधिक संभावना है, इसलिए फिर से दस्तावेज़ देखें।

साथ ही मैंने कुछ संदर्भों में यूयूआईडी मूल्यों को अनदेखा करते हुए देखा है क्योंकि डीबी मानता है कि इस प्रकार का कोई सार्थक आदेश नहीं है। यदि आपको ऐसा लगता है कि यह मामला स्पष्ट रूप से UUID को ऑर्डरिंग क्लॉज में एक स्ट्रिंग प्रकार के लिए कास्ट करता है, या इसके चारों ओर कुछ अन्य फ़ंक्शन को लपेटता है, जैसे CHECKSUM()SQL सर्वर (इसमें से एक छोटा सा प्रदर्शन अंतर भी हो सकता है क्योंकि ऑर्डर करना होगा 32-बिट का मान 128-बिट वाला नहीं है, हालाँकि उस लाभ का CHECKSUM()मूल्य प्रति मूल्य चलने की लागत पहले है कि मैं आपको परीक्षण करने के लिए छोड़ दूंगा)।

पक्षीय लेख

यदि आप मनमाने ढंग से लेकिन कुछ बार-बार दोहराने योग्य आदेश चाहते हैं, तो पंक्तियों में डेटा के कुछ अपेक्षाकृत अनियंत्रित सबसेट द्वारा आदेश दें। उदाहरण के लिए या तो इन नामों को एक मनमाना लेकिन दोहराए जाने वाले क्रम में लौटाया जाएगा:

SELECT display_name FROM tr_person ORDER BY CHECKSUM(display_name), display_name -- order by the checksum of some of the row's data
SELECT display_name FROM tr_person ORDER BY SUBSTRING(display_name, LEN(display_name)/2, 128) -- order by part of the name field, but not in any an obviously recognisable order)

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

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

प्रदर्शन

जैसा कि EBarr बताते हैं, उपरोक्त में से किसी के साथ प्रदर्शन समस्याएँ हो सकती हैं। कुछ पंक्तियों से अधिक के लिए आप सही क्रम में वापस पढ़ी जा रही पंक्तियों की अनुरोधित संख्या से पहले टेम्पर्ड करने के लिए आउटपुट को देखने के लिए लगभग तैयार हैं, जिसका अर्थ है कि भले ही आप शीर्ष 10 की तलाश कर रहे हों, आपको पूर्ण सूचकांक मिल सकता है स्कैन (या बदतर, टेबल स्कैन) लिखने की एक बड़ी ब्लॉक के साथ होता है। उत्पादन में इस का उपयोग करने से पहले यथार्थवादी आंकड़ों के साथ बेंचमार्क करने के लिए, अधिकांश चीजों के साथ, यह महत्वपूर्ण रूप से महत्वपूर्ण हो सकता है।


14

यह एक पुराना सवाल है, लेकिन चर्चा का एक पहलू याद आ रहा है, मेरी राय में - निष्पादन। ORDER BY NewId()सामान्य उत्तर है। वे जोड़ते हैं किसी को मिलता है कल्पना है कि तुम सच लपेट चाहिए NewID()में CheckSum(), आप प्रदर्शन के लिए पता है,!

इस विधि के साथ समस्या यह है कि आप अभी भी एक पूर्ण सूचकांक स्कैन और फिर डेटा की एक पूर्ण प्रकार की गारंटी दे रहे हैं। यदि आपने किसी गंभीर डेटा वॉल्यूम के साथ काम किया है तो यह तेजी से महंगा हो सकता है। इस विशिष्ट निष्पादन योजना को देखें, और ध्यान दें कि किस प्रकार आपके समय का 96% हिस्सा लेता है ...

यहाँ छवि विवरण दर्ज करें

आपको यह बताने के लिए कि मैं किस पैमाने पर काम करता हूँ, मैं आपको एक डेटाबेस से दो उदाहरण देता हूँ।

  • TableA - में 2500 डेटा पृष्ठों पर 50,000 पंक्तियाँ हैं। यादृच्छिक क्वेरी 42ms में 145 रीड उत्पन्न करती है।
  • तालिका B - में 114,000 डेटा पृष्ठों पर 1.2 मिलियन पंक्तियाँ हैं। Order By newid()इस टेबल पर दौड़ने से 53,700 रीड्स निकलते हैं और 16 सेकंड लगते हैं।

कहानी की नैतिकता यह है कि यदि आपके पास बड़ी टेबल हैं (अरबों पंक्तियों के बारे में सोचें) या इस क्वेरी को चलाने की आवश्यकता है, तो अक्सर newid()विधि टूट जाती है। तो लड़का क्या करे?

TABLESAMPLE () से मिलो

SQL 2005 में नामक एक नई क्षमता TABLESAMPLEबनाई गई थी। मैंने केवल एक लेख पर चर्चा करते हुए देखा है कि यह उपयोग है ... अधिक होना चाहिए। MSDN डॉक्स यहाँ । पहला उदाहरण:

SELECT Top (20) *
FROM Northwind..Orders TABLESAMPLE(20 PERCENT)
ORDER BY NEWID()

टेबल सैंपल के पीछे का विचार यह है कि आप जो सब्मिट साइज़ पूछते हैं, वह लगभग आपको दे दें । एसक्यूएल प्रत्येक डेटा पेज को नंबर देता है और उन पृष्ठों के एक्स प्रतिशत का चयन करता है। आपके द्वारा वापस प्राप्त की जाने वाली पंक्तियों की वास्तविक संख्या चयनित पृष्ठों में मौजूद चीज़ों के आधार पर भिन्न हो सकती है।

तो मैं इसका उपयोग कैसे करूँ? एक सबसेट आकार का चयन करें जो आपकी ज़रूरत की पंक्तियों की संख्या को कवर करता है, फिर एक जोड़ें Top()। विचार यह है कि आप अपने ginormous तालिका को महंगे प्रकार से पहले छोटा बना सकते हैं ।

व्यक्तिगत रूप से मैं इसे अपनी तालिका के आकार को सीमित करने के लिए उपयोग कर रहा हूं। तो उस मिलियन रो टेबल top(20)...TABLESAMPLE(20 PERCENT)पर क्वेरी को ड्रॉप करते हुए 5600 को 1600ms में पढ़ता है। एक REPEATABLE()विकल्प भी है जहां आप पृष्ठ चयन के लिए "बीज" पास कर सकते हैं। यह एक स्थिर नमूना चयन में परिणाम होना चाहिए।

वैसे भी, बस यह चर्चा में जोड़ा जाना चाहिए। आशा है कि यह किसी की मदद करता है।


यह एक स्केलेबल रैंडम-ऑर्डरिंग क्वेरी लिखने में सक्षम होना अच्छा होगा जो न केवल स्केल करता है बल्कि छोटे डेटा सेट के साथ काम करता है। ऐसा लगता है कि आपके पास मैन्युअल रूप से स्विच करना है और TABLESAMPLE()आपके पास कितना डेटा है, इसके आधार पर नहीं । मुझे नहीं लगता है कि TABLESAMPLE(x ROWS)यह भी सुनिश्चित करेगा कि कम से कम x पंक्तियों को वापस कर दिया जाए क्योंकि प्रलेखन कहता है कि "पंक्तियों की वास्तविक संख्या जो वापस लौटी हैं वे काफी भिन्न हो सकती हैं। यदि आप एक छोटी संख्या को निर्दिष्ट करते हैं, जैसे कि 5, तो आपको नमूने में परिणाम प्राप्त नहीं हो सकता है। ”- तो ROWSक्या वास्तव में वाक्यविन्यास अभी भी PERCENTअंदर एक मुखौटा है?
बिंकी जू

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

3

कई तालिकाओं में अपेक्षाकृत घने (कुछ लापता मूल्य) अनुक्रमित संख्यात्मक आईडी स्तंभ होते हैं।

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

वर्णन करने के लिए, निम्न कोड उपयोगकर्ताओं के स्टैक ओवरफ्लो तालिका से 100 अलग-अलग यादृच्छिक उपयोगकर्ताओं को चुनता है, जिसमें 8,123,937 पंक्तियाँ होती हैं।

पहला कदम आईडी मानों की सीमा निर्धारित करना है, जो सूचकांक के कारण एक कुशल संचालन है:

DECLARE 
    @MinID integer,
    @Range integer,
    @Rows bigint = 100;

--- Find the range of values
SELECT
    @MinID = MIN(U.Id),
    @Range = 1 + MAX(U.Id) - MIN(U.Id)
FROM dbo.Users AS U;

रेंज क्वेरी

योजना सूचकांक के प्रत्येक छोर से एक पंक्ति पढ़ती है।

अब हम रेंज में 100 अलग-अलग यादृच्छिक आईडी बनाते हैं (उपयोगकर्ताओं की तालिका में पंक्तियों के मिलान के साथ) और उन पंक्तियों को वापस करते हैं:

WITH Random (ID) AS
(
    -- Find @Rows distinct random user IDs that exist
    SELECT DISTINCT TOP (@Rows)
        Random.ID
    FROM dbo.Users AS U
    CROSS APPLY
    (
        -- Random ID
        VALUES (@MinID + (CONVERT(integer, CRYPT_GEN_RANDOM(4)) % @Range))
    ) AS Random (ID)
    WHERE EXISTS
    (
        SELECT 1
        FROM dbo.Users AS U2
            -- Ensure the row continues to exist
            WITH (REPEATABLEREAD)
        WHERE U2.Id = Random.ID
    )
)
SELECT
    U3.Id,
    U3.DisplayName,
    U3.CreationDate
FROM Random AS R
JOIN dbo.Users AS U3
    ON U3.Id = R.ID
-- QO model hint required to get a non-blocking flow distinct
OPTION (MAXDOP 1, USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'));

यादृच्छिक पंक्तियों क्वेरी

योजना से पता चलता है कि इस मामले में 100 मिलान पंक्तियों को खोजने के लिए 601 यादृच्छिक संख्या की आवश्यकता थी। यह बहुत जल्दी है:

तालिका 'उपयोगकर्ता'। स्कैन काउंट 1, लॉजिकल रीड 1937, फिजिकल रीड 2, रीड-फॉरवर्ड रीड्स 408
टेबल 'वर्कटेबल'। स्कैन काउंट 0, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0
टेबल 'वर्कफाइल'। स्कैन काउंट 0, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, रीड-फॉरवर्ड रीड्स 0

 SQL सर्वर निष्पादन समय:
   सीपीयू समय = 0 एमएस, बीता हुआ समय = 9 एमएस।

इसे स्टैक एक्सचेंज डेटा एक्सप्लोरर पर आज़माएं।


0

जैसा कि मैंने इस लेख में बताया है , SQL परिणाम सेट में फेरबदल करने के लिए, आपको एक डेटाबेस-विशिष्ट फ़ंक्शन कॉल का उपयोग करने की आवश्यकता होती है।

ध्यान दें कि एक रैंडम फ़ंक्शन का उपयोग करके सेट किए गए एक बड़े परिणाम को बहुत धीमा हो सकता है, इसलिए सुनिश्चित करें कि आप छोटे परिणाम सेट पर ऐसा करते हैं।

यदि आपको एक बड़े परिणाम सेट को फेरबदल करना है और बाद में इसे सीमित करना है, तो ORDER BY खंड में यादृच्छिक फ़ंक्शन के बजाय SQL सर्वर TABLESAMPLEमें SQL सर्वर का उपयोग करना बेहतर है ।

इसलिए, यह मानते हुए कि हमारे पास निम्नलिखित डेटाबेस तालिका है:

यहाँ छवि विवरण दर्ज करें

और songतालिका में निम्नलिखित पंक्तियाँ :

| id | artist                          | title                              |
|----|---------------------------------|------------------------------------|
| 1  | Miyagi & Эндшпиль ft. Рем Дигга | I Got Love                         |
| 2  | HAIM                            | Don't Save Me (Cyril Hahn Remix)   |
| 3  | 2Pac ft. DMX                    | Rise Of A Champion (GalilHD Remix) |
| 4  | Ed Sheeran & Passenger          | No Diggity (Kygo Remix)            |
| 5  | JP Cooper ft. Mali-Koa          | All This Love                      |

SQL सर्वर पर, आपको NEWIDफ़ंक्शन का उपयोग करने की आवश्यकता है , जैसा कि निम्नलिखित उदाहरण द्वारा चित्रित किया गया है:

SELECT
    CONCAT(CONCAT(artist, ' - '), title) AS song
FROM song
ORDER BY NEWID()

SQL सर्वर पर पूर्वोक्त SQL क्वेरी चलाते समय, हम निम्नलिखित परिणाम सेट प्राप्त करने जा रहे हैं:

| song                                              |
|---------------------------------------------------|
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |
| JP Cooper ft. Mali-Koa - All This Love            |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |

ध्यान दें कि गीतों को यादृच्छिक क्रम में सूचीबद्ध किया जा रहा NEWIDहै, ORDER BY क्लॉज द्वारा उपयोग किए जाने वाले फ़ंक्शन कॉल के लिए धन्यवाद ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.