Pradeep Adigan ensimmäinen ehdotus, ORDER BY NEWID()
, on hyvä ja jotain, jota olen käyttänyt aiemmin tästä syystä.
Ole varovainen RAND()
:n käytön kanssa – monissa yhteyksissä se suoritetaan vain kerran per lauseke, joten ORDER BY RAND()
:llä ei ole mitään vaikutusta (koska saat saman tuloksen RAND():sta jokaiselle riville).
Esimerkiksi:
SELECT display_name, RAND() FROM tr_person
palauttaa jokaisen nimen henkilötaulustamme ja ”satunnaisluvun”, joka on sama jokaiselle riville. Luku tosin vaihtelee joka kerta, kun kysely suoritetaan, mutta se on sama jokaiselle riville joka kerta.
Kokeilemalla:
SELECT display_name FROM tr_person ORDER BY RAND(), display_name
näytän, että sama pätee myös ORDER BY
-lausekkeessa käytetyn RAND()
:n kohdalla, ja kokeilen:
SELECT display_name FROM tr_person ORDER BY RAND(), display_name
Tulokset ovat edelleen järjestyksessä nimen mukaan, mikä osoittaa, että aiemmalla lajittelukenttällä (jonka odotetaan olevan satunnaista) ei ole mitään vaikutusta, joten sen arvo on ilmeisesti oletettavasti aina sama.
Ordering by NEWID()
toimii kuitenkin, koska jos NEWID() ei olisi aina uudelleenarvioitu UUID:ien tarkoitus menisi rikki, kun lisättäisiin monta uutta riviä yhteen statemnt:iin, joiden avaimena ovat uniikit tunnisteet, joten:
SELECT display_name FROM tr_person ORDER BY NEWID()
järjestelee nimet ”satunnaisesti”.
Muut DBMS
Ylläoleva pätee MSSQL:ään (2005 ja 2008 ainakin, ja muistaakseni myös 2000). Uuden UUID:n palauttavan funktion pitäisi evaluoitua joka kerta kaikissa DBMS:ssä NEWID() on MSSQL:n alla, mutta tämä kannattaa tarkistaa dokumentaatiosta ja/tai omilla testeillä. Muiden mielivaltaisen tuloksen funktioiden, kuten RAND():n, käyttäytyminen vaihtelee todennäköisemmin DBMS:ien välillä, joten tarkista jälleen kerran dokumentaatio.
Olen myös nähnyt, että UUID-arvojen järjestys jätetään huomioimatta joissakin yhteyksissä, koska DB olettaa, että tyypillä ei ole mielekästä järjestystä. Jos huomaat, että näin on, käännä UUID-tunnus nimenomaisesti merkkijonotyypiksi järjestyslausekkeessa tai kiedo sen ympärille jokin muu funktio, kuten CHECKSUM()
SQL Serverissä (tämäkin saattaa aiheuttaa pientä suorituskykyeroa, koska järjestys tehdään 32-bittisille arvoille, ei 128-bittisille, mutta jätän sinun testattavaksesi, onko tästä saatava hyöty suurempi kuin kustannus, joka aiheutuu siitä, että ajetaan CHECKSUM()
jokaista arvoa kohti ensin).
Sivuhuomautus
Jos haluat mielivaltaisen, mutta jokseenkin toistettavan järjestyksen, järjestä jonkin suhteellisen kontrolloimattoman osajoukon mukaan itse riveillä olevista tiedoista. Esimerkiksi jompikumpi näistä palauttaa nimet mielivaltaisessa mutta toistettavassa järjestyksessä:
Tätä temppua voidaan käyttää myös mielivaltaisempien tulosten saamiseen funktioista, jotka eivät salli ei-deterministisiä kutsuja, kuten NEWID(), runkonsa sisällä. Tästäkään ei todennäköisesti ole usein hyötyä reaalimaailmassa, mutta se voi olla kätevä, jos haluat funktion palauttavan jotain satunnaista ja ”satunnainen” riittää hyvin (mutta muista varovasti säännöt, jotka määrittelevät, milloin käyttäjän määrittelemät funktiot evaluoituvat, eli yleensä vain kerran riviä kohti, tai tuloksesi eivät välttämättä ole sitä, mitä odotat/vaatit).
Suorituskyky
Kuten EBarr huomauttaa, suorituskykyongelmia voi esiintyä missä tahansa edellä mainituissa toiminnoissa. Useamman kuin muutaman rivin kohdalla on lähes varmaa, että tuloste spoolataan tempdb:hen ennen kuin pyydetty määrä rivejä luetaan takaisin oikeassa järjestyksessä, mikä tarkoittaa, että vaikka etsit top 10:tä, saatat huomata, että täysi indeksin skannaus (tai pahempaa, taulukon skannaus) tapahtuu yhdessä valtavan kirjoituslohkon kanssa tempdb:hen. Siksi voi olla elintärkeää, kuten useimpien asioiden kohdalla, tehdä vertailuanalyysi realistisella datalla, ennen kuin käytät tätä tuotannossa.