J'ai une table SQL Server avec environ 50 000 lignes. Je veux sélectionner environ 5 000 de ces rangées au hasard. J'ai pensé à un moyen compliqué: créer une table temporaire avec une colonne "nombre aléatoire", y copier ma table, parcourir la table temporaire, mettre à jour chaque ligne avec Rand()
<0.1. Je cherche un moyen plus simple de le faire, en une seule déclaration si possible.
Cet article suggère d'utiliser la fonction NEWID()
. Cela semble prometteur, mais je ne vois pas comment je pourrais sélectionner un certain pourcentage de rangées de manière fiable.
Quelqu'un a déjà fait ça avant? Des idées?
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 la jointure, ce qui sur une grande table avec un pourcentage de sélection faible devrait être raisonnable.
En fonction de vos besoins, TABLESAMPLE
vous proposera presque aussi aléatoire et de meilleures performances . Ceci est disponible sur MS SQL Server 2005 et versions ultérieures.
TABLESAMPLE
renverra des données de pages aléatoires au lieu de lignes aléatoires. Par conséquent, deos ne récupérera même pas les données qu’elles ne renverront 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 que ce ne sera pas avec newid()
.
Veuillez garder à l’esprit que ce n’est pas aussi aléatoire que la méthode newid()
mais vous donnera un bon échantillonnage.
Voir la page MSDN .
newid ()/order by fonctionnera, mais sera très coûteux pour les ensembles de résultats volumineux 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 regroupement des résultats (toutes les lignes d'une page seront retournées).
Pour obtenir un échantillon véritablement aléatoire plus performant, le meilleur moyen consiste à filtrer les lignes de manière aléatoire. J'ai trouvé l'exemple de code suivant dans l'article LIMITATION DES ENSEMBLES DE R&EACUTE;SULTATS &AGRAVE; L'AIDE DE TABLESAMPLE de la documentation en ligne de SQL Server:
Si vous voulez vraiment un échantillon aléatoire de lignes individuelles, modifiez votre requête en filtrer les lignes au hasard, au lieu de en utilisant TABLESAMPLE. Par exemple, le La requête suivante utilise le NEWID fonction pour retourner environ un pour cent des lignes de la Table Sales.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 CHECKSUM pour que NEWID () évalue une fois par ligne à réaliser un échantillonnage par rangée . L'expression CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) donne à .__ une valeur float aléatoire comprise entre 0 et 1.
Lorsqu’il est exécuté sur une table de 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 en utilisant TABLESAMPLE, vous obtiendrez les meilleures performances. Sinon, utilisez la méthode newid ()/filter. newid ()/order by devrait être le dernier recours si vous avez un ensemble de résultats important.
Sélection aléatoire de lignes dans une grande table sur MSDN, vous disposez d'une solution simple, bien articulée, qui répond aux problèmes de performances à 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 complique l'approche de CHECKSUM) et souhaitez un échantillon plus aléatoire que TABLESAMPLE en tant que tel, et souhaitez également une vitesse supérieure à celle de CHECKSUM, vous pouvez vous contenter d'une fusion des Les 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, il s’agit du compromis le plus simple entre le caractère aléatoire (ce n’est pas vraiment, je le sais) et la vitesse. Variez le pourcentage de TABLESAMPLE (ou les lignes) selon vos besoins - 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)
Commandez simplement la table par un nombre aléatoire et obtenez les 5 000 premières lignes en utilisant TOP
.
SELECT TOP 5000 * FROM [Table] ORDER BY newid();
METTRE &AGRAVE; JOUR
Juste essayé et un appel newid()
est suffisant - pas besoin de tous les moulages et de tous les calculs.
Ce lien présente une comparaison intéressante entre Orderby (NEWID ()) et d’autres méthodes pour les tables contenant 1, 7 et 13 millions de lignes.
Souvent, lorsque des questions sur la sélection de 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()
Toutefois, la requête NEWID présente un gros inconvénient lorsque vous l'utilisez pour des tables volumineuses. 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 pose deux problèmes:
Ce dont vous avez besoin est un moyen de sélectionner des lignes de manière aléatoire, sans utiliser tempdb et avec le ralentissement de la taille du tableau. 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 est de générer un nombre aléatoire compris entre 0 et 99 pour chaque ligne du tableau, puis de sélectionner 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 .
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 initiale de la graine et d’une somme de contrôle qui me permet de donner des résultats correctement aléatoires sans le coût de NEWID ():
SELECT TOP [number]
FROM table_name
ORDER BY Rand(CHECKSUM(*) * Rand())
Je n'ai pas encore vu cette variation dans les réponses. J'avais une contrainte supplémentaire où il me fallait, à partir d'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 très légèrement plus lent que Rand(checksum(*))
, vous pouvez donc ne pas vouloir l'utiliser pour des jeux d'enregistrements volumineux.
Sélection avec la graine initiale:
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 à partir d'une graine, cela semble fonctionner.
Essaye ça:
SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()
Il apparaît que newid () ne peut pas être utilisé dans la clause where, cette solution nécessite donc une requête interne:
SELECT *
FROM (
SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
FROM MyTable
) vw
WHERE Rnd % 100 < 10 --10%
Je l'utilisais dans une sous-requête et il me renvoyait les mêmes lignes dans une sous-requête
SELECT ID ,
( SELECT TOP 1
ImageURL
FROM SubTable
ORDER BY NEWID()
) AS ImageURL,
GETUTCDATE() ,
1
FROM Mytable
alors j'ai résolu avec notamment la variable de table parent dans où
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, saisissez le nombre requis (ou tous les enregistrements) et utilisez la fonction de lecture aléatoire de PHP au lieu de la randomisation dans la requête. Je ne sais pas si .net a une fonction équivalente, mais si c'est le cas, utilisez-le si vous utilisez .net
ORDER BY Rand () peut être très pénalisant en termes de performances, selon le nombre d'enregistrements impliqués.