J'ai une table SQL Server contenant environ 50 000 lignes. Je souhaite sélectionner environ 5 000 de ces lignes au hasard. J'ai pensé à une manière compliquée, créer une table temporaire avec une colonne "nombre aléatoire", copier ma table dans celle-ci, parcourir la table temporaire et mettre à jour chaque ligne avec RAND()
, puis sélectionner dans cette table où la colonne de nombre aléatoire < 0,1. Je cherche un moyen plus simple de le faire, dans une seule déclaration si possible.
Cet article suggère d'utiliser la NEWID()
fonction. Cela semble prometteur, mais je ne vois pas comment je pourrais sélectionner de manière fiable un certain pourcentage de lignes.
Quelqu'un a déjà fait ça avant? Des idées?
sql
sql-server
random
John M Gant
la source
la source
Réponses:
En réponse au commentaire "pure corbeille" concernant les grandes tables: vous pouvez le faire comme ça pour améliorer les performances.
Le coût de ceci sera l'analyse des valeurs clés plus le coût de jointure, qui sur une grande table avec un petit pourcentage de sélection devrait être raisonnable.
la source
[yourPk]
réfère-t-il? EDIT: Nvm, compris ... Clé primaire. Durrrnewid()
coût d'E / S de l'estimation de tri sera très élevé et affectera les performances.Selon vos besoins,
TABLESAMPLE
vous obtiendrez des performances presque aussi aléatoires et meilleures. ceci est disponible sur MS SQL Server 2005 et versions ultérieures.TABLESAMPLE
renverra des données à partir de pages aléatoires au lieu de lignes aléatoires et par conséquent, les deos ne récupéreront même pas les données qu'elles ne renverront pas.Sur une très grande table, j'ai testé
a pris plus de 20 minutes.
a pris 2 minutes.
Les performances s'amélioreront également sur des échantillons plus petits
TABLESAMPLE
alors que ce ne sera pas le cas avecnewid()
.Veuillez garder à l'esprit que ce n'est pas aussi aléatoire que la
newid()
méthode mais vous donnera un échantillonnage décent.Voir la page MSDN .
la source
newid () / order by fonctionnera, mais sera très coûteux pour les grands ensembles de résultats car il doit générer un identifiant pour chaque ligne, puis les trier.
TABLESAMPLE () est bon du point de vue des performances, mais vous obtiendrez un agrégat de résultats (toutes les lignes d'une page seront retournées).
Pour un véritable échantillon aléatoire plus performant, la meilleure façon est de filtrer les lignes de manière aléatoire. J'ai trouvé l'exemple de code suivant dans l'article de documentation en ligne de SQL Server Limitation des ensembles de résultats à l'aide de TABLESAMPLE :
Lorsqu'il est exécuté sur une table avec 1 000 000 lignes, voici mes résultats:
Si vous pouvez vous en sortir en utilisant TABLESAMPLE, cela vous donnera les meilleures performances. Sinon, utilisez la méthode newid () / filter. newid () / order by devrait être le dernier recours si vous avez un grand ensemble de résultats.
la source
NewID()
soit évalué qu'une seule fois, au lieu de par ligne, ce que je n'aime pas ...La sélection aléatoire de lignes dans une grande table sur MSDN propose une solution simple et bien articulée qui répond aux problèmes de performances à grande échelle.
la source
RAND()
ne retourne pas la même valeur pour chaque ligne (ce qui irait à l'BINARY_CHECKSUM()
encontre de la logique). Est-ce parce qu'il est appelé dans une autre fonction plutôt que de faire partie de la clause SELECT?rand()
ou une combinaison des éléments ci-dessus - mais je me suis détourné de cette solution pour cette raison. De plus, le nombre de résultats variait de 1 à 5, ce qui pourrait donc ne pas être acceptable dans certains scénarios.RAND()
renvoie la même valeur pour chaque ligne (c'est pourquoi cette solution est rapide). Cependant, les lignes avec des sommes de contrôle binaires qui sont très proches les unes des autres courent un risque élevé de générer des résultats de somme de contrôle similaires, provoquant une agglutination lorsqu'elleRAND()
est petite. Par exemple,(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
. Si vos données souffrent de ce problème, multipliezBINARY_CHECKSUM
par 9923.Ce lien présente une comparaison intéressante entre Orderby (NEWID ()) et d'autres méthodes pour les tables avec 1, 7 et 13 millions de lignes.
Souvent, lorsque des questions sur la façon de sélectionner des lignes aléatoires sont posées dans des groupes de discussion, la requête NEWID est proposée; il est simple et fonctionne très bien pour les petites tables.
Cependant, la requête NEWID présente un gros inconvénient lorsque vous l'utilisez pour de grandes tables. La clause ORDER BY entraîne la copie de toutes les lignes de la table dans la base de données tempdb, où elles sont triées. Cela provoque deux problèmes:
Ce dont vous avez besoin est un moyen de sélectionner des lignes au hasard qui n'utilisera pas tempdb et ne deviendra pas beaucoup plus lent à mesure que la table s'agrandit. Voici une nouvelle idée sur la façon de procéder:
L'idée de base de cette requête est que nous voulons générer un nombre aléatoire entre 0 et 99 pour chaque ligne du tableau, puis choisir toutes les lignes dont le nombre aléatoire est inférieur à la valeur du pourcentage spécifié. Dans cet exemple, nous voulons environ 10% des lignes sélectionnées au hasard; par conséquent, nous choisissons toutes les lignes dont le nombre aléatoire est inférieur à 10.
Veuillez lire l'article complet dans MSDN .
la source
Si vous (contrairement à l'OP) avez besoin d'un nombre spécifique d'enregistrements (ce qui rend l'approche CHECKSUM difficile) et souhaitez un échantillon plus aléatoire que TABLESAMPLE en lui-même, et souhaitez également une meilleure vitesse que CHECKSUM, vous pouvez vous contenter d'une fusion des Les méthodes TABLESAMPLE et NEWID (), comme ceci:
Dans mon cas, c'est le compromis le plus simple entre le caractère aléatoire (ce n'est pas vraiment, je sais) et la vitesse. Faites varier le pourcentage (ou les lignes) TABLESAMPLE selon le cas - plus le pourcentage est élevé, plus l'échantillon est aléatoire, mais attendez-vous à une baisse linéaire de la vitesse. (Notez que TABLESAMPLE n'acceptera pas de variable)
la source
Commandez simplement la table par un nombre aléatoire et obtenez les 5 000 premières lignes en utilisant
TOP
.METTRE À JOUR
Je viens de l'essayer et un
newid()
appel suffit - pas besoin de tous les lancers et de tous les calculs.la source
C'est une combinaison de l'idée de départ et d'une somme de contrôle, qui me semble donner des résultats correctement aléatoires sans le coût de NEWID ():
la source
Dans MySQL, vous pouvez faire ceci:
la source
Je n'ai pas encore tout à fait vu cette variation dans les réponses. J'avais une contrainte supplémentaire là où j'avais besoin, étant donné une graine initiale, de sélectionner le même ensemble de lignes à chaque fois.
Pour MS SQL:
Exemple minimum:
Temps d'exécution normalisé: 1,00
Exemple avec NewId ():
Temps d'exécution normalisé: 1,02
NewId()
est insignifiamment plus lent querand(checksum(*))
, il est donc possible que vous ne souhaitiez pas l'utiliser avec de grands jeux d'enregistrements.Sélection avec semence initiale:
Si vous devez sélectionner le même ensemble en fonction d'une graine, cela semble fonctionner.
la source
Essaye ça:
la source
Il semble que newid () ne peut pas être utilisé dans la clause where, donc cette solution nécessite une requête interne:
la source
Je l'utilisais dans la sous-requête et cela m'a renvoyé les mêmes lignes dans la sous-requête
puis j'ai résolu avec l'inclusion de la variable de la table parent où
Notez la condition où
la source
Le langage de traitement côté serveur utilisé (par exemple PHP, .net, etc.) n'est pas spécifié, mais s'il s'agit de PHP, récupérez le nombre requis (ou tous les enregistrements) et au lieu de randomiser dans la requête, utilisez la fonction de lecture aléatoire de PHP. Je ne sais pas si .net a une fonction équivalente mais s'il l'utilise alors si vous utilisez .net
ORDER BY RAND () peut avoir toute une pénalité de performance, selon le nombre d'enregistrements impliqués.
la source
Cela fonctionne pour moi:
la source
select top 10 percent from table_name order by rand()
, mais cela ne fonctionne pas non plus car rand () renvoie la même valeur sur toutes les lignes.