J'ai une requête où je veux que les enregistrements résultants soient commandés au hasard. Il utilise un index clusterisé, donc si je n'inclus pas, order by
il retournera probablement des enregistrements dans l'ordre de cet index. Comment puis-je garantir un ordre de ligne aléatoire?
Je comprends qu'il ne sera probablement pas "vraiment" aléatoire, le pseudo-aléatoire est assez bon pour mes besoins.
sql-server
goric
la source
la source
CryptGenRandom
à la fin. dba.stackexchange.com/a/208069/3690La première suggestion de Pradeep Adiga
ORDER BY NEWID()
, est très bien et quelque chose que j'ai utilisé dans le passé pour cette raison.Soyez prudent avec l'utilisation
RAND()
- dans de nombreux contextes, elle n'est exécutée qu'une fois par instruction, elleORDER BY RAND()
n'aura donc aucun effet (car vous obtenez le même résultat de RAND () pour chaque ligne).Par exemple:
renvoie chaque nom de notre table person et un nombre "aléatoire", qui est le même pour chaque ligne. Le nombre varie chaque fois que vous exécutez la requête, mais il est le même pour chaque ligne à chaque fois.
Pour montrer que c'est la même chose avec
RAND()
utilisé dans uneORDER BY
clause, j'essaye:Les résultats sont toujours classés par nom, ce qui indique que le champ de tri précédent (celui qui devrait être aléatoire) n'a aucun effet et a donc probablement toujours la même valeur.
La commande par
NEWID()
fonctionne cependant, car si NEWID () n'était pas toujours réévalué, le but des UUID serait rompu lors de l'insertion de nombreuses nouvelles lignes dans un même statut avec des identifiants uniques lors de leur clé, donc:ne commande les noms « au hasard ».
Autres SGBD
Ce qui précède est vrai pour MSSQL (2005 et 2008 au moins, et si je me souviens bien 2000 également). Une fonction renvoyant un nouvel UUID doit être évaluée à chaque fois dans tous les SGBD NEWID () est sous MSSQL mais cela vaut la peine de le vérifier dans la documentation et / ou par vos propres tests. Le comportement d'autres fonctions de résultats arbitraires, comme RAND (), est plus susceptible de varier entre les SGBD, alors vérifiez à nouveau la documentation.
J'ai également vu que l'ordre des valeurs UUID était ignoré dans certains contextes, car la base de données suppose que le type n'a pas d'ordre significatif. Si vous trouvez que c'est le cas, convertissez explicitement l'UUID en un type de chaîne dans la clause de commande, ou encapsulez une autre fonction autour de celle-ci comme
CHECKSUM()
dans SQL Server (il peut y avoir une petite différence de performances par rapport à cela car la commande sera effectuée sur une valeur 32 bits et non une valeur 128 bits, bien que si l'avantage de cela l'emporte sur le coût de fonctionnementCHECKSUM()
par valeur, je vous laisse tester).Note latérale
Si vous voulez un ordre arbitraire mais quelque peu répétable, triez par un sous-ensemble relativement incontrôlé des données dans les lignes elles-mêmes. Par exemple, l'un ou l'autre retournera les noms dans un ordre arbitraire mais répétable:
Les commandes arbitraires mais répétables ne sont pas souvent utiles dans les applications, mais peuvent être utiles pour tester si vous voulez tester du code sur les résultats dans une variété d'ordres mais que vous voulez pouvoir répéter chaque exécution de la même manière plusieurs fois (pour obtenir un timing moyen résultats sur plusieurs exécutions, ou tester qu'un correctif que vous avez apporté au code supprime un problème ou une inefficacité précédemment mis en évidence par un jeu de résultats d'entrée particulier, ou simplement pour tester que votre code est "stable", c'est-à-dire qu'il renvoie le même résultat à chaque fois si envoyé les mêmes données dans un ordre donné).
Cette astuce peut également être utilisée pour obtenir des résultats plus arbitraires à partir de fonctions, qui n'autorisent pas les appels non déterministes comme NEWID () dans leur corps. Encore une fois, ce n'est pas quelque chose qui est susceptible d'être souvent utile dans le monde réel, mais qui pourrait être utile si vous voulez qu'une fonction renvoie quelque chose de aléatoire et "random-ish" est assez bon (mais attention à vous rappeler les règles qui déterminent lorsque les fonctions définies par l'utilisateur sont évaluées, c'est-à-dire généralement une seule fois par ligne, ou vos résultats peuvent ne pas être ceux que vous attendez / exigez).
Performance
Comme le souligne EBarr, il peut y avoir des problèmes de performance avec l'un des éléments ci-dessus. Pour plus de quelques lignes, vous êtes presque assuré de voir la sortie mise en file d'attente vers tempdb avant la lecture du nombre de lignes demandé dans le bon ordre, ce qui signifie que même si vous recherchez le top 10, vous pouvez trouver un index complet l'analyse (ou pire, l'analyse de table) se produit avec un énorme bloc d'écriture dans tempdb. C'est pourquoi il peut être d'une importance vitale, comme pour la plupart des choses, de comparer des données réalistes avant de les utiliser en production.
la source
C'est une vieille question, mais un aspect de la discussion manque, à mon avis - PERFORMANCE.
ORDER BY NewId()
est la réponse générale. Lorsque l'imagination de quelqu'un get ils ajoutent que vous devriez vraiment envelopperNewID()
dansCheckSum()
, vous le savez, pour la performance!Le problème avec cette méthode, c'est que vous êtes toujours garanti une analyse complète de l'index, puis un tri complet des données. Si vous avez travaillé avec un volume de données sérieux, cela peut rapidement devenir coûteux. Regardez ce plan d'exécution typique, et notez comment le tri prend 96% de votre temps ...
Pour vous donner une idée de la façon dont cela évolue, je vais vous donner deux exemples d'une base de données avec laquelle je travaille.
Order By newid()
sur cette table génère 53 700 lectures et prend 16 secondes.La morale de l'histoire est que si vous avez de grandes tables (pensez à des milliards de lignes) ou si vous devez exécuter cette requête fréquemment, la
newid()
méthode tombe en panne. Alors, qu'est-ce qu'un garçon à faire?Rencontrez TABLESAMPLE ()
Dans SQL 2005, une nouvelle fonctionnalité appelée a
TABLESAMPLE
été créée. Je n'ai vu qu'un seul article discutant de son utilisation ... il devrait y en avoir plus. Documents MSDN ici . D'abord un exemple:L'idée derrière l'échantillon de table est de vous donner approximativement la taille de sous-ensemble que vous demandez. SQL numérote chaque page de données et sélectionne X pour cent de ces pages. Le nombre réel de lignes que vous récupérez peut varier en fonction de ce qui existe dans les pages sélectionnées.
Alors, comment dois-je l'utiliser? Sélectionnez une taille de sous-ensemble qui couvre plus que le nombre de lignes dont vous avez besoin, puis ajoutez a
Top()
. L'idée est que vous pouvez faire paraître votre table ginormous plus petite avant le tri coûteux.Personnellement, je l'ai utilisé pour limiter la taille de ma table. Ainsi, sur cette table de millions de lignes,
top(20)...TABLESAMPLE(20 PERCENT)
la requête tombe à 5600 lectures en 1600 ms. Il y a aussi uneREPEATABLE()
option où vous pouvez passer un "Seed" pour la sélection de page. Il devrait en résulter une sélection d'échantillon stable.Quoi qu'il en soit, je pensais simplement que cela devrait être ajouté à la discussion. J'espère que cela aide quelqu'un.
la source
TABLESAMPLE()
fonction de la quantité de données dont vous disposez. Je ne pense pas que celaTABLESAMPLE(x ROWS)
garantirait même qu'au moins desx
lignes soient renvoyées car la documentation indique: «Le nombre réel de lignes renvoyées peut varier considérablement. Si vous spécifiez un petit nombre, tel que 5, vous risquez de ne pas recevoir de résultats dans l'échantillon. »- laROWS
syntaxe n'est-elle donc vraiment qu'un masque à l'PERCENT
intérieur?De nombreuses tables ont une colonne d'ID numérique indexée relativement dense (quelques valeurs manquantes).
Cela nous permet de déterminer la plage de valeurs existantes et de choisir des lignes à l'aide de valeurs d'ID générées de manière aléatoire dans cette plage. Cela fonctionne mieux lorsque le nombre de lignes à renvoyer est relativement petit et que la plage de valeurs d'ID est densément peuplée (donc les chances de générer une valeur manquante sont suffisamment petites).
Pour illustrer, le code suivant choisit 100 utilisateurs aléatoires distincts dans la table d'utilisateurs Stack Overflow, qui contient 8 123 937 lignes.
La première étape consiste à déterminer la plage de valeurs ID, une opération efficace grâce à l'index:
Le plan lit une ligne à chaque extrémité de l'index.
Maintenant, nous générons 100 ID aléatoires distincts dans la plage (avec des lignes correspondantes dans la table des utilisateurs) et renvoyons ces lignes:
Le plan montre que dans ce cas, 601 nombres aléatoires étaient nécessaires pour trouver 100 lignes correspondantes. C'est assez rapide:
Essayez-le sur l'explorateur de données Exchange Stack.
la source
Comme je l'ai expliqué dans cet article , pour mélanger l'ensemble de résultats SQL, vous devez utiliser un appel de fonction spécifique à la base de données.
Donc, en supposant que nous ayons la table de base de données suivante:
Et les lignes suivantes du
song
tableau:Sur SQL Server, vous devez utiliser la
NEWID
fonction, comme illustré par l'exemple suivant:Lors de l'exécution de la requête SQL susmentionnée sur SQL Server, nous allons obtenir le jeu de résultats suivant:
la source