Sélectionnez n lignes aléatoires dans la table SQL Server
J'ai une table SQL Server avec environ 50 000 lignes. Je veux sélectionner environ 5 000 de ces lignes au hasard. J'ai pensé à une manière compliquée, en créant une table temporaire avec une colonne "nombre aléatoire", en copiant ma table dans celle-ci, en faisant une boucle dans la table temporaire et en mettant à jour chaque ligne avec RAND()
, puis en sélectionnant à partir de cette table où la colonne de nombre aléatoire
Cet article suggérons d'utiliser la fonction NEWID()
. 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?
15 réponses
select top 10 percent * from [yourtable] order by newid()
En réponse au commentaire "pure trash" concernant les grandes tables: vous pouvez le faire comme ceci pour améliorer les performances.
select * from [yourtable] where [yourPk] in
(select top 10 percent [yourPk] from [yourtable] order by newid())
Le coût de ceci sera l'analyse clé des valeurs plus le coût de jointure, qui sur une grande table avec un petit pourcentage de sélection devrait être raisonnable.
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
retournera des données à partir de pages aléatoires au lieu de lignes aléatoires et donc deos ne récupérera même pas les données qu'il ne retournera pas.
Sur une très grande table, j'ai testé
select top 1 percent * from [tablename] order by newid()
A pris plus de 20 minutes.
select * from [tablename] tablesample(1 percent)
A pris 2 minutes.
Les performances s'amélioreront également sur les échantillons plus petits dans TABLESAMPLE
alors qu'elles ne le seront pas avec newid()
.
Gardez à l'esprit que ce n'est pas aussi aléatoire que la méthode newid()
mais vous donnera un échantillonnage décent.
Voir la page MSDN .
Newid () / order by fonctionnera, mais sera très coûteux pour les grands ensembles de résultats car il doit générer un id pour chaque ligne, puis les trier.
TABLESAMPLE () est bon du point de vue des performances, mais vous obtiendrez des résultats groupés (toutes les lignes d'une page seront retournées).
Pour un échantillon aléatoire vrai plus performant, le meilleur moyen est de filtrer les lignes au hasard. J'ai trouvé l'exemple de code suivant dans L'article SQL Server Books Onlinelimiter les ensembles de résultats en utilisant TABLESAMPLE:
Si vous voulez vraiment un échantillon aléatoire de lignes individuelles, modifiez votre requête pour filtrer les lignes au hasard, au lieu de utilisation de TABLESAMPLE. Par exemple, l' la requête suivante utilise le NEWID fonction pour renvoyer environ un pourcentage des lignes de l' Commerciale.SalesOrderDetail:
SELECT * FROM Sales.SalesOrderDetail WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)
La colonne SalesOrderID est incluse dans l'expression de somme de contrôle de sorte que Newid() évalue une fois par ligne à réaliser l'échantillonnage sur un par ligne. L'expression CAST(CHECKSUM (NEWID(), SalesOrderID) & 0x7fffffff comme flottant / CAST (0x7fffffff comme int) évalue à une valeur flottante aléatoire comprise entre 0 et 1.
Lorsqu'il est exécuté sur une table avec 1 000 000 lignes, voici mes résultats:
SET STATISTICS TIME ON
SET STATISTICS IO ON
/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()
/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)
/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
Si vous pouvez vous en sortir avec TABLESAMPLE, il 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 jeu de résultats.
La sélection aléatoire de lignes à partir d'une grande Table sur MSDN a une solution simple et bien articulée qui répond aux problèmes de performance à grande échelle.
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
Si vous (contrairement à L'OP) avez besoin d'un nombre spécifique d'enregistrements (ce qui rend l'approche CHECKSUM difficile) et désirez un échantillon plus aléatoire que TABLESAMPLE ne fournit par lui-même, et aussi une meilleure vitesse que CHECKSUM, vous pouvez vous contenter d'une fusion des méthodes TABLESAMPLE et NEWID (), comme ceci:
DECLARE @sampleCount int = 50
SET STATISTICS TIME ON
SELECT TOP (@sampleCount) *
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()
SET STATISTICS TIME OFF
Dans mon cas, c'est le compromis le plus simple entre le hasard (ce n'est pas vraiment, je sais) et la vitesse. Variez le pourcentage D'échantillon de tableau (ou les lignes) 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 une variable)
Il suffit de commander la table par un nombre aléatoire et d'obtenir les 5 000 premières lignes en utilisant TOP
.
SELECT TOP 5000 * FROM [Table] ORDER BY newid();
Mise à jour
Juste essayé et un appel newid()
est suffisant-pas besoin de tous les moulages et tous les calculs.
Ce lien a 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; c'est simple et fonctionne très bien pour les petites tables.
SELECT TOP 10 PERCENT *
FROM Table1
ORDER BY NEWID()
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 tempdb la base de données, où ils sont triés. Cela provoque deux problèmes:
- L'opération de tri a généralement un coût élevé associé. Le tri peut utiliser beaucoup d'e / s de disque et peut fonctionner pendant une longue période.
- dans le pire des cas, tempdb peut manquer d'espace. Dans le dans le meilleur des cas, tempdb peut prendre une grande quantité d'espace disque cela ne sera jamais récupéré sans une commande manuelle de rétrécissement.
Ce dont vous avez besoin est un moyen de sélectionner des lignes au hasard qui ne seront pas utilisées tempdb et ne sera pas beaucoup plus lent que la table devient plus grande. Voici une nouvelle idée sur la façon de le faire:
SELECT * FROM Table1
WHERE (ABS(CAST(
(BINARY_CHECKSUM(*) *
RAND()) as int)) % 100) < 10
L'idée de base de cette requête, c'est que nous voulons générer un nombre aléatoire entre 0 et 99 pour chaque ligne du tableau, puis sélectionnez toutes les lignes dont le nombre aléatoire est inférieur à la valeur spécifiée pour cent. Dans cet exemple, nous voulons environ 10 pour cent 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 .
Dans MySQL, vous pouvez faire ceci:
SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;
C'est une combinaison de l'idée de départ initiale et d'une somme de contrôle, qui me semble donner des résultats correctement aléatoires sans le coût de NEWID ():
SELECT TOP [number]
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())
Essayez ceci:
SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
N'a pas encore vu cette variation dans les réponses. J'avais une contrainte supplémentaire où j'ai besoin, étant donné une graine initiale, pour sélectionner le même ensemble de lignes à chaque fois.
Pour MS SQL:
Exemple Minimum:
select top 10 percent *
from table_name
order by rand(checksum(*))
Temps D'exécution normalisé: 1.00
Newid () exemple:
select top 10 percent *
from table_name
order by newid()
Temps D'exécution normalisé: 1.02
NewId()
est légèrement plus lent que rand(checksum(*))
, donc vous ne voudrez peut-être pas l'utiliser contre de grands ensembles d'enregistrements.
Sélection avec initiale Semences:
declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */
select top 10 percent *
from table_name
order by rand(checksum(*) % @seed) /* any other math function here */
Si vous devez sélectionner le même ensemble donné une graine, cela semble fonctionner.
Cela fonctionne pour moi:
SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]
Il semble que newid () ne puisse pas être utilisé dans la clause where, donc cette solution nécessite une requête interne:
SELECT *
FROM (
SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
FROM MyTable
) vw
WHERE Rnd % 100 < 10 --10%
Je l'utilisais dans la sous-requête et il m'a renvoyé les mêmes lignes dans la sous-requête
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
Ensuite, j'ai résolu avec l'inclusion de la variable de table parent dans where
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
Where Mytable.ID>0
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
Notez la condition où
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 la requête, utilisez la fonction shuffle de PHP. Je ne sais pas si. Net a une fonction équivalente mais si elle l'utilise alors si vous utilisez. net
ORDER BY RAND() peut avoir une pénalité de performance, en fonction du nombre d'enregistrements impliqués.