Pradeep Adigas första förslag, ORDER BY NEWID()
, är bra och något jag har använt tidigare av denna anledning.
Var försiktig med att använda RAND()
– i många sammanhang exekveras det bara en gång per yttrande så ORDER BY RAND()
kommer inte att ha någon effekt (eftersom du får samma resultat av RAND() för varje rad).
Till exempel:
SELECT display_name, RAND() FROM tr_person
återger varje namn från vår persontabell och ett ”slumpmässigt” nummer, som är detsamma för varje rad. Antalet varierar visserligen varje gång du kör frågan, men är detsamma för varje rad varje gång.
För att visa att detsamma gäller för RAND()
som används i en ORDER BY
-klausul försöker jag:
SELECT display_name FROM tr_person ORDER BY RAND(), display_name
Resultaten är fortfarande ordnade efter namnet, vilket tyder på att det tidigare sorteringsfältet (det som förväntades vara slumpmässigt) inte har någon effekt, så det har antagligen alltid samma värde.
Ordna efter NEWID()
fungerar dock, för om NEWID() inte alltid omvärderades skulle syftet med UUID:er brytas när man lägger in många nya rader i en statemnt med unika identifierare som nyckel, så:
SELECT display_name FROM tr_person ORDER BY NEWID()
ordnar namnen ”slumpmässigt”.
Andra DBMS
Ovanstående gäller för MSSQL (2005 och 2008 åtminstone, och om jag minns rätt 2000 också). En funktion som returnerar ett nytt UUID bör utvärderas varje gång i alla DBMS NEWID() är under MSSQL men det är värt att verifiera detta i dokumentationen och/eller genom egna tester. Beteendet hos andra funktioner med godtyckliga resultat, som RAND(), varierar troligen mer mellan DBMS, så kontrollera återigen dokumentationen.
Jag har också sett att ordering av UUID-värden ignoreras i vissa sammanhang eftersom DB:n utgår från att typen inte har någon meningsfull ordering. Om du upptäcker att så är fallet, kastar du uttryckligen UUID till en strängtyp i beställningsklausulen, eller lindar någon annan funktion runt den som CHECKSUM()
i SQL Server (det kan finnas en liten prestandaskillnad från detta också eftersom beställningen kommer att göras på 32-bitars värden och inte 128-bitars värden, men om fördelen med detta uppväger kostnaden för att köra CHECKSUM()
per värde först ska jag låta dig testa).
Side Note
Om du vill ha en godtycklig men någorlunda repeterbar ordningsföljd, ordna efter någon relativt okontrollerad delmängd av data i själva raderna. Till exempel kommer någon av dessa att återge namnen i en godtycklig men upprepningsbar ordning:
Arbiträra men repeterbara ordningar är inte ofta användbara i tillämpningar, men kan vara användbara vid testning om du vill testa någon kod på resultat i olika ordningar men vill kunna upprepa varje körning på samma sätt flera gånger (för att få fram genomsnittliga tidsresultat över flera körningar, eller för att testa att en korrigering som du har gjort av koden tar bort ett problem eller en ineffektivitet som tidigare framhävts av en viss inmatningsresultatuppsättning, eller bara för att testa att din kod är ”stabil” i den bemärkelsen att den returnerar samma resultat varje gång om den skickas med samma data i en viss ordning).
Detta trick kan också användas för att få mer godtyckliga resultat från funktioner, som inte tillåter icke-deterministiska anrop som NEWID() inom sin kropp. Återigen är detta inte något som troligen kommer att vara ofta användbart i den verkliga världen, men det kan vara praktiskt om du vill att en funktion ska returnera något slumpmässigt och ”slumpmässigt” är tillräckligt bra (men var noga med att komma ihåg reglerna som bestämmer när användardefinierade funktioner evalveras, dvs. vanligtvis bara en gång per rad, eller så kanske dina resultat inte blir vad du förväntar dig/kräver).
Prestationer
Som EBarr påpekar kan det finnas prestandaproblem med någon av de ovan nämnda metoderna. För mer än några få rader är det nästan garanterat att utdata spoolas ut till tempdb innan det begärda antalet rader läses tillbaka i rätt ordning, vilket innebär att även om du letar efter de tio bästa raderna kan det hända att en fullständig indexavsökning (eller ännu värre, en tabellavsökning) sker tillsammans med ett enormt block av skrivning till tempdb. Därför kan det vara oerhört viktigt, som med de flesta saker, att göra en benchmark med realistiska data innan man använder detta i produktionen.