j'avais l'habitude de toujours le faire en php après les résultats de la requête de sql ... c'est probablement beaucoup plus rapide pour le traitement selon l'annexe 1 de la solution
-1 pour compter sur order by rand()ou équivalents dans tous les dbs: |. également mentionné ici .
AD7six
20
Il y a dix ans, un gars a dit que l'utilisation ORDER BY RAND()était mauvaise ...
trejder
ORDER BY NEWID () semble être nettement plus lent sur SQL Server. Ma requête ressemble à ceci: sélectionnez le top 1000 C.CustomerId, CL.LoginName du client C jointure interne LinkedAccount LA sur C.CustomerId = LA.CustomerId jointure interne CustomerLogin CL sur C.CustomerId = CL.CustomerId groupe par C.CustomerId, CL. LoginName ayant count (*)> 1 commande par NEWID () La suppression de la ligne "order by NEWID ()" renvoie les résultats beaucoup plus rapidement.
Ben Power
3
Pour SQLite, utilisez la fonction RANDOM ().
Slam du
10
Ces solutions ne sont pas évolutives. Ils sont O(n)avec nle nombre d'enregistrements dans la table. Imaginez que vous ayez 1 million d'enregistrements, voulez-vous vraiment générer 1 million de nombres aléatoires ou d'identifiants uniques? Je préfère utiliser COUNT()et impliquer cela dans une nouvelle LIMITexpression avec un seul nombre aléatoire.
Christian Hujer
174
Des solutions comme Jeremies:
SELECT*FROMtableORDERBY RAND() LIMIT 1
fonctionnent, mais ils ont besoin d'une analyse séquentielle de toute la table (car la valeur aléatoire associée à chaque ligne doit être calculée - afin que la plus petite puisse être déterminée), ce qui peut être assez lent pour des tables même de taille moyenne. Ma recommandation serait d'utiliser une sorte de colonne numérique indexée (de nombreuses tables en ont comme clés primaires), puis d'écrire quelque chose comme:
SELECT*FROMtableWHERE num_value >= RAND()*(SELECT MAX (num_value )FROMtable)ORDERBY num_value LIMIT 1
Cela fonctionne en temps logarithmique, quelle que soit la taille de la table, s'il num_valueest indexé. Une mise en garde: cela suppose qu'il num_valueest également distribué dans la plage 0..MAX(num_value). Si votre jeu de données s'écarte fortement de cette hypothèse, vous obtiendrez des résultats asymétriques (certaines lignes apparaîtront plus souvent que d'autres).
La deuxième suggestion n'est pas aléatoire. Vous ne pouvez pas prédire la ligne qui sera choisie, mais si vous deviez miser, vous parieriez sur la deuxième ligne. Et vous ne parieriez jamais sur la dernière ligne, c'est le moins susceptible d'être choisi quelle que soit la distribution de votre num_value et la taille de votre table.
Etienne Racine
1
Je sais que les fonctions RAND () ne sont généralement pas de très haute qualité, mais à part cela, pouvez-vous expliquer pourquoi la sélection ne serait pas aléatoire?
Grey Panther
13
Le premier est incorrect dans SQL Server. La fonction RAND () n'est invoquée qu'une seule fois par requête et non une fois par ligne. Il sélectionne donc toujours la première ligne (essayez-le).
Jeff Walker Code Ranger
3
Le second suppose également que toutes les lignes sont prises en compte: il est possible qu'il choisisse une ligne qui a été supprimée.
Sam Rueby
3
@ Sam.Rueby En fait, num_value> = RAND () ... la limite 1 garantit que les lignes vides seront ignorées jusqu'à ce qu'elle trouve la ligne existante.
ghord
62
Je ne sais pas à quel point c'est efficace, mais je l'ai déjà utilisé:
SELECTTOP1*FROM MyTable ORDERBY newid()
Étant donné que les GUID sont assez aléatoires, la commande signifie que vous obtenez une ligne aléatoire.
J'utilise MS SQL Server, SELECT TOP 1 * FROM some_table_name ORDER BY NEWID () a très bien fonctionné pour moi, merci pour les conseils!
C'est exactement la même chose queORDER BY RAND() LIMIT 1
Ken Bloom
6
Ceci est également très spécifique à la base de données car il utilise TOP 1et newid().
Grey
12
C'est une mauvaise idée. Cette méthode n'utilisera pas d'index sauf si chaque colonne est indexée individuellement. Une table contenant 100 millions d'enregistrements peut prendre très longtemps pour obtenir un enregistrement.
Changer
1
@Switch et quelle solution proposeriez-vous?
Akmal Salikhov
31
ORDERBY NEWID()
prend 7.4 milliseconds
WHERE num_value >= RAND()*(SELECT MAX(num_value)FROMtable)
La deuxième option ne sélectionnera pas la dernière ligne. Je ne sais pas pourquoi - simplement le souligner.
Voldemort
7
@Voldemort: rand()renvoie un nombre à virgule flottante noù 0 < n < 1. En supposant qu'il num_values'agit d'un entier, la valeur de retour de rand() * max(num_value)sera également contrainte à un entier, tronquant ainsi tout ce qui suit la virgule décimale. Par conséquent, rand() * max(num_value)sera toujours inférieur à max(num_value), c'est pourquoi la dernière ligne ne sera jamais sélectionnée.
Ian Kemp
Je ne serai pas efficace si mes données sont supprimées souvent - si je trouve un écart, je devrai réexécuter toute la requête.
Loic Coenen
1
@IanKemp Question stupide, alors pourquoi ne pas simplement utiliser SELECT MAX (num_value) + 1 ?? Puisque rand (ou RANDOM dans la plupart des cas) renvoie [0,1), vous obtiendrez la gamme complète de valeurs. Aussi, oui, vous avez raison, je dois corriger une requête.
tekHedd
13
Vous n'avez pas dit quel serveur vous utilisez. Dans les anciennes versions de SQL Server, vous pouvez utiliser ceci:
selecttop1*from mytable orderby newid()
Dans SQL Server 2005 et versions ultérieures, vous pouvez utiliser TABLESAMPLEpour obtenir un échantillon aléatoire répétable:
SELECT FirstName, LastName
FROM Contact
TABLESAMPLE (1ROWS);
@Andrew Hedges: COMMANDER PAR NEWID () est trop cher
Andrei Rînea
10
Pour SQL Server
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 Limiter les ensembles de résultats à l'aide de TABLESAMPLE :
Si vous voulez vraiment un échantillon aléatoire de lignes individuelles, modifiez votre requête pour filtrer les lignes au hasard, au lieu d'utiliser TABLESAMPLE. Par exemple, la requête suivante utilise la fonction NEWID pour renvoyer environ un pour cent des lignes de la table Sales.SalesOrderDetail:
La colonne SalesOrderID est incluse dans l'expression CHECKSUM afin que NEWID () évalue une fois par ligne pour réaliser l'échantillonnage sur une ligne. L'expression CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) est évaluée comme 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:
SETSTATISTICS TIME ONSETSTATISTICS IO ON/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/SELECTTOP1PERCENT Number
FROM Numbers
ORDERBY newid()/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/SELECT Number
FROM Numbers
TABLESAMPLE (1PERCENT)/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/SELECT Number
FROM Numbers
WHERE0.01>= CAST(CHECKSUM(NEWID(), Number)&0x7fffffffAS float)/ CAST (0x7fffffffAS int)SETSTATISTICS IO OFFSETSTATISTICS TIME OFF
Si vous pouvez vous en tirer avec 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.
Cette solution prend également en charge le renvoi de lignes aléatoires lorsque la valeur numérique indexée utilisée dans la clause where ci-dessus n'est pas également distribuée; donc même si cela prend presque le même temps (constant) que d'utiliser où id_value> = RAND () * MAX (id_value), c'est mieux.
guido
Pour autant que je sache, cela ne fonctionne pas en temps constant, il fonctionne en temps linéaire. Dans le pire des cas, @n est égal au nombre de lignes de la table et "SELECT * FROM table LIMIT?, 1" évalue @n - 1 lignes jusqu'à ce qu'il atteigne la dernière.
Andres Riofrio
3
La meilleure façon est de mettre une valeur aléatoire dans une nouvelle colonne juste à cette fin, et d'utiliser quelque chose comme ça (pseude code + SQL):
randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")
C'est la solution employée par le code MediaWiki. Bien sûr, il existe un certain biais par rapport aux valeurs plus petites, mais ils ont constaté qu'il suffisait d'enrouler la valeur aléatoire à zéro lorsqu'aucune ligne n'est extraite.
La solution newid () peut nécessiter une analyse complète de la table afin que chaque ligne puisse se voir attribuer un nouveau guid, qui sera beaucoup moins performant.
La solution rand () peut ne pas fonctionner du tout (c'est-à-dire avec MSSQL) car la fonction sera évaluée une seule fois et chaque ligne se verra attribuer le même numéro "aléatoire".
Envelopper autour lorsque vous obtenez 0 résultats fournit un échantillon prouvablement aléatoire (pas seulement "assez bon"). Cette solution s'adapte presque aux requêtes à plusieurs lignes (pensez au "shuffle de fête"). Le problème est que les résultats ont tendance à être sélectionnés à plusieurs reprises dans les mêmes groupes. Pour contourner ce problème, vous devez redistribuer les nombres aléatoires que vous venez d'utiliser. Vous pouvez tricher en gardant une trace de randomNo et en le définissant sur max (caractère aléatoire) à partir des résultats, mais ensuite p (ligne i sur la requête 1 ET ligne i sur la requête 2) == 0, ce qui n'est pas juste. Permettez-moi de faire quelques calculs, et je vous répondrai avec un plan vraiment juste.
alsuren
3
Pour SQL Server 2005 et 2008, si nous voulons un échantillon aléatoire de lignes individuelles (à partir de la documentation en ligne ):
SELECT ID FROMTABLEWHERE ID >= My_Generated_Random ORDERBY ID LIMIT 1
Notez qu'il vérifiera toutes les lignes dont les ID sont ÉGAUX ou SUPÉRIEURS à la valeur choisie. Il est également possible de rechercher la ligne dans le tableau et d'obtenir un ID égal ou inférieur à My_Generated_Random, puis de modifier la requête comme suit:
SELECT ID FROMTABLEWHERE ID <= My_Generated_Random ORDERBY ID DESC LIMIT 1
Que se passerait-il si l'ID aléatoire généré n'existe plus dans la table? Les lignes supprimées ou passives que vous ne souhaitez pas montrer à l'utilisateur causeraient des problèmes.
Ebleme
Rien. Vous obtenez le numéro d'identification le plus proche, pas exact. Si vous considérez id = 1 comme supprimé, échangez 1 avec minimum.
forsberg
2
Comme indiqué dans le commentaire de @ BillKarwin sur la réponse de @ cnu ...
Lors de la combinaison avec un LIMIT, j'ai trouvé qu'il fonctionne beaucoup mieux (au moins avec PostgreSQL 9.1) pour JOIN avec un ordre aléatoire plutôt que pour ordonner directement les lignes réelles: par exemple
SELECT*FROM tbl_post AS t
JOIN...JOIN(SELECT id, CAST(-2147483648* RANDOM()AS integer)AS rand
FROM tbl_post
WHERE create_time >=1349928000) r ON r.id = t.id
WHERE create_time >=1349928000AND...ORDERBY r.rand
LIMIT 100
Assurez-vous simplement que le «r» génère une valeur «rand» pour chaque valeur de clé possible dans la requête complexe qui lui est associée, mais limitez toujours le nombre de lignes de «r» dans la mesure du possible.
Le CAST en tant qu'entier est particulièrement utile pour PostgreSQL 9.2 qui a une optimisation de tri spécifique pour les types flottants entiers et simple précision.
La plupart des solutions ici visent à éviter le tri, mais elles doivent toujours effectuer une analyse séquentielle sur une table.
Il existe également un moyen d'éviter le balayage séquentiel en passant au balayage d'index. Si vous connaissez la valeur d'index de votre ligne aléatoire, vous pouvez obtenir le résultat presque instantanément. Le problème est - comment deviner une valeur d'index.
La solution suivante fonctionne sur PostgreSQL 8.4:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))
limit 1;
I solution ci-dessus, vous devinez 10 différentes valeurs d'index aléatoires de la plage 0 .. [dernière valeur de id].
Le nombre 10 est arbitraire - vous pouvez utiliser 100 ou 1000 car cela (étonnamment) n'a pas un grand impact sur le temps de réponse.
Il y a aussi un problème - si vous avez des identifiants clairsemés, vous pourriez manquer . La solution est d' avoir un plan de sauvegarde :) Dans ce cas, un pur ancien ordre par requête random (). Lorsque l'ID combiné ressemble à ceci:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))unionall(select*from cms_refs orderby random() limit 1)
limit 1;
Pas la clause union ALL . Dans ce cas, si la première partie renvoie des données, la seconde n'est JAMAIS exécutée!
En retard, mais arrivé ici via Google, donc pour la postérité, je vais ajouter une solution alternative.
Une autre approche consiste à utiliser TOP deux fois, avec des ordres alternés. Je ne sais pas s'il s'agit de "SQL pur", car il utilise une variable dans le TOP, mais il fonctionne dans SQL Server 2008. Voici un exemple que j'utilise par rapport à un tableau de mots de dictionnaire, si je veux un mot aléatoire.
SELECTTOP1
word
FROM(SELECTTOP(@idx)
word
FROM
dbo.DictionaryAbridged WITH(NOLOCK)ORDERBY
word DESC)AS D
ORDERBY
word ASC
Bien sûr, @idx est un entier généré de manière aléatoire qui va de 1 à COUNT (*) sur la table cible, inclusivement. Si votre colonne est indexée, vous en profiterez également. Un autre avantage est que vous pouvez l'utiliser dans une fonction, car NEWID () n'est pas autorisé.
Enfin, la requête ci-dessus s'exécute dans environ 1/10 du temps d'exécution d'une requête de type NEWID () sur la même table. YYMV.
Après avoir testé de nombreuses réponses, je pense que c'est la meilleure. Il semble être rapide et choisir un bon nombre aléatoire à chaque fois. Cela semble similaire à la deuxième suggestion de @GreyPanther ci-dessus, mais cette réponse prend plus de nombres aléatoires.
Jeff Baker le
1
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.
NewId()est insignifiamment plus lent que rand(checksum(*)), donc vous ne voudrez peut-être pas l'utiliser contre de grands jeux d'enregistrements.
Sélection avec semence initiale:
declare@seed int
set@seed = Year(getdate())* month(getdate())/* any other initial seed here */selecttop10percent*from table_name
orderby rand(checksum(*)% seed)/* any other math function here */
Si vous devez sélectionner le même ensemble en fonction d'une graine, cela semble fonctionner.
Dans SQL Server, vous pouvez combiner TABLESAMPLE avec NEWID () pour obtenir un assez bon caractère aléatoire et toujours avoir de la vitesse. Ceci est particulièrement utile si vous ne voulez vraiment que 1 ou un petit nombre de lignes.
Avec SQL Server 2012+, vous pouvez utiliser la requête OFFSET FETCH pour le faire pour une seule ligne aléatoire
select*from MyTable ORDERBY id OFFSET n ROWFETCH NEXT 1ROWS ONLY
où id est une colonne d'identité et n est la ligne que vous voulez - calculée comme un nombre aléatoire entre 0 et count () - 1 de la table (le décalage 0 est la première ligne après tout)
Cela fonctionne avec des trous dans les données de la table, tant que vous disposez d'un index avec lequel travailler pour la clause ORDER BY. C'est aussi très bon pour le caractère aléatoire - car vous travaillez vous-même pour passer, mais les inconvénients des autres méthodes ne sont pas présents. De plus, les performances sont assez bonnes, sur un ensemble de données plus petit, elles résistent bien, même si je n'ai pas essayé de tests de performances sérieux contre plusieurs millions de lignes.
Il y a dix ans (2005), un gars a dit que l'utilisation ORDER BY RAND()était mauvaise ...
trejder
0
Je dois être d'accord avec CD-MaN: L'utilisation de "ORDER BY RAND ()" fonctionnera bien pour les petites tables ou lorsque vous effectuez votre SELECT seulement quelques fois.
J'utilise également la technique "num_value> = RAND () * ...", et si je veux vraiment avoir des résultats aléatoires, j'ai une colonne "aléatoire" spéciale dans le tableau que je mets à jour une fois par jour environ. Cette exécution unique de MISE À JOUR prendra un certain temps (en particulier parce que vous devrez avoir un index sur cette colonne), mais c'est beaucoup plus rapide que de créer des nombres aléatoires pour chaque ligne à chaque exécution de la sélection.
Soyez prudent car TableSample ne retourne pas réellement un échantillon aléatoire de lignes. Il dirige votre requête pour regarder un échantillon aléatoire des pages de 8 Ko qui composent votre ligne. Ensuite, votre requête est exécutée par rapport aux données contenues dans ces pages. En raison de la façon dont les données peuvent être regroupées sur ces pages (ordre d'insertion, etc.), cela pourrait conduire à des données qui ne sont pas réellement un échantillon aléatoire.
Il semble que bon nombre des idées énumérées utilisent encore la commande
Cependant, si vous utilisez une table temporaire, vous pouvez attribuer un index aléatoire (comme la plupart des solutions l'ont suggéré), puis récupérer le premier qui est supérieur à un nombre arbitraire entre 0 et 1.
Par exemple (pour DB2):
WITH TEMP AS(SELECT COMLUMN, RAND()AS IDX FROMTABLE)SELECTCOLUMNFROMTABLEWHERE IDX >.5FETCH FIRST 1ROW ONLY
Après avoir envisagé cette solution, j'ai trouvé une faille fondamentale dans ma logique. Cela retournerait systématiquement les mêmes petites valeurs de configuration, près du début du tableau, car je suppose que s'il y avait une distribution égale entre 0 et 1, il y a 50% de chances que la première ligne réponde à ces critères.
Il existe une meilleure solution pour Oracle au lieu d'utiliser dbms_random.value, alors qu'elle nécessite une analyse complète pour ordonner les lignes par dbms_random.value et elle est assez lente pour les grandes tables.
Pour SQL Server 2005 et supérieur, étendre la réponse de @ GreyPanther aux cas où num_valuen'a pas de valeurs continues. Cela fonctionne aussi pour les cas où nous n'avons pas réparti les ensembles de données de manière égale et quand il num_valuene s'agit pas d'un nombre mais d'un identifiant unique.
WITH CTE_Table (SelRow, num_value)AS(SELECT ROW_NUMBER()OVER(ORDERBY ID)AS SelRow, num_value FROMtable)SELECT*FROMtableWhere num_value =(SELECTTOP1 num_value FROM CTE_Table WHERE SelRow >= RAND()*(SELECT MAX(SelRow)FROM CTE_Table))
Réponses:
Voir cet article: SQL pour sélectionner une ligne aléatoire dans une table de base de données . Il passe par des méthodes pour le faire dans MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 et Oracle (ce qui suit est copié à partir de ce lien):
Sélectionnez une ligne aléatoire avec MySQL:
Sélectionnez une ligne aléatoire avec PostgreSQL:
Sélectionnez une ligne aléatoire avec Microsoft SQL Server:
Sélectionnez une ligne aléatoire avec IBM DB2
Sélectionnez un enregistrement aléatoire avec Oracle:
la source
order by rand()
ou équivalents dans tous les dbs: |. également mentionné ici .ORDER BY RAND()
était mauvaise ...O(n)
avecn
le nombre d'enregistrements dans la table. Imaginez que vous ayez 1 million d'enregistrements, voulez-vous vraiment générer 1 million de nombres aléatoires ou d'identifiants uniques? Je préfère utiliserCOUNT()
et impliquer cela dans une nouvelleLIMIT
expression avec un seul nombre aléatoire.Des solutions comme Jeremies:
fonctionnent, mais ils ont besoin d'une analyse séquentielle de toute la table (car la valeur aléatoire associée à chaque ligne doit être calculée - afin que la plus petite puisse être déterminée), ce qui peut être assez lent pour des tables même de taille moyenne. Ma recommandation serait d'utiliser une sorte de colonne numérique indexée (de nombreuses tables en ont comme clés primaires), puis d'écrire quelque chose comme:
Cela fonctionne en temps logarithmique, quelle que soit la taille de la table, s'il
num_value
est indexé. Une mise en garde: cela suppose qu'ilnum_value
est également distribué dans la plage0..MAX(num_value)
. Si votre jeu de données s'écarte fortement de cette hypothèse, vous obtiendrez des résultats asymétriques (certaines lignes apparaîtront plus souvent que d'autres).la source
Je ne sais pas à quel point c'est efficace, mais je l'ai déjà utilisé:
Étant donné que les GUID sont assez aléatoires, la commande signifie que vous obtenez une ligne aléatoire.
la source
ORDER BY RAND() LIMIT 1
TOP 1
etnewid()
.prend
7.4 milliseconds
prend
0.0065 milliseconds
!J'irai certainement avec cette dernière méthode.
la source
rand()
renvoie un nombre à virgule flottanten
où0 < n < 1
. En supposant qu'ilnum_value
s'agit d'un entier, la valeur de retour derand() * max(num_value)
sera également contrainte à un entier, tronquant ainsi tout ce qui suit la virgule décimale. Par conséquent,rand() * max(num_value)
sera toujours inférieur àmax(num_value)
, c'est pourquoi la dernière ligne ne sera jamais sélectionnée.Vous n'avez pas dit quel serveur vous utilisez. Dans les anciennes versions de SQL Server, vous pouvez utiliser ceci:
Dans SQL Server 2005 et versions ultérieures, vous pouvez utiliser
TABLESAMPLE
pour obtenir un échantillon aléatoire répétable:la source
Pour SQL Server
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 Limiter les 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 tirer avec 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
Si possible, utilisez des instructions stockées pour éviter l'inefficacité des deux index sur RND () et créer un champ de numéro d'enregistrement.
la source
La meilleure façon est de mettre une valeur aléatoire dans une nouvelle colonne juste à cette fin, et d'utiliser quelque chose comme ça (pseude code + SQL):
C'est la solution employée par le code MediaWiki. Bien sûr, il existe un certain biais par rapport aux valeurs plus petites, mais ils ont constaté qu'il suffisait d'enrouler la valeur aléatoire à zéro lorsqu'aucune ligne n'est extraite.
La solution newid () peut nécessiter une analyse complète de la table afin que chaque ligne puisse se voir attribuer un nouveau guid, qui sera beaucoup moins performant.
La solution rand () peut ne pas fonctionner du tout (c'est-à-dire avec MSSQL) car la fonction sera évaluée une seule fois et chaque ligne se verra attribuer le même numéro "aléatoire".
la source
Pour SQL Server 2005 et 2008, si nous voulons un échantillon aléatoire de lignes individuelles (à partir de la documentation en ligne ):
la source
Au lieu d' utiliser RAND (), comme cela n'est pas recommandé , vous pouvez simplement obtenir un ID max (= Max):
obtenir un aléatoire entre 1..Max (= My_Generated_Random)
puis exécutez ce SQL:
Notez qu'il vérifiera toutes les lignes dont les ID sont ÉGAUX ou SUPÉRIEURS à la valeur choisie. Il est également possible de rechercher la ligne dans le tableau et d'obtenir un ID égal ou inférieur à My_Generated_Random, puis de modifier la requête comme suit:
la source
Comme indiqué dans le commentaire de @ BillKarwin sur la réponse de @ cnu ...
Lors de la combinaison avec un LIMIT, j'ai trouvé qu'il fonctionne beaucoup mieux (au moins avec PostgreSQL 9.1) pour JOIN avec un ordre aléatoire plutôt que pour ordonner directement les lignes réelles: par exemple
Assurez-vous simplement que le «r» génère une valeur «rand» pour chaque valeur de clé possible dans la requête complexe qui lui est associée, mais limitez toujours le nombre de lignes de «r» dans la mesure du possible.
Le CAST en tant qu'entier est particulièrement utile pour PostgreSQL 9.2 qui a une optimisation de tri spécifique pour les types flottants entiers et simple précision.
la source
La plupart des solutions ici visent à éviter le tri, mais elles doivent toujours effectuer une analyse séquentielle sur une table.
Il existe également un moyen d'éviter le balayage séquentiel en passant au balayage d'index. Si vous connaissez la valeur d'index de votre ligne aléatoire, vous pouvez obtenir le résultat presque instantanément. Le problème est - comment deviner une valeur d'index.
La solution suivante fonctionne sur PostgreSQL 8.4:
I solution ci-dessus, vous devinez 10 différentes valeurs d'index aléatoires de la plage 0 .. [dernière valeur de id].
Le nombre 10 est arbitraire - vous pouvez utiliser 100 ou 1000 car cela (étonnamment) n'a pas un grand impact sur le temps de réponse.
Il y a aussi un problème - si vous avez des identifiants clairsemés, vous pourriez manquer . La solution est d' avoir un plan de sauvegarde :) Dans ce cas, un pur ancien ordre par requête random (). Lorsque l'ID combiné ressemble à ceci:
Pas la clause union ALL . Dans ce cas, si la première partie renvoie des données, la seconde n'est JAMAIS exécutée!
la source
En retard, mais arrivé ici via Google, donc pour la postérité, je vais ajouter une solution alternative.
Une autre approche consiste à utiliser TOP deux fois, avec des ordres alternés. Je ne sais pas s'il s'agit de "SQL pur", car il utilise une variable dans le TOP, mais il fonctionne dans SQL Server 2008. Voici un exemple que j'utilise par rapport à un tableau de mots de dictionnaire, si je veux un mot aléatoire.
Bien sûr, @idx est un entier généré de manière aléatoire qui va de 1 à COUNT (*) sur la table cible, inclusivement. Si votre colonne est indexée, vous en profiterez également. Un autre avantage est que vous pouvez l'utiliser dans une fonction, car NEWID () n'est pas autorisé.
Enfin, la requête ci-dessus s'exécute dans environ 1/10 du temps d'exécution d'une requête de type NEWID () sur la même table. YYMV.
la source
Vous pouvez également essayer d'utiliser la
new id()
fonction.Écrivez simplement votre requête et utilisez l'ordre par
new id()
fonction. C'est assez aléatoire.la source
Pour MySQL pour obtenir un enregistrement aléatoire
Plus de détails http://jan.kneschke.de/projects/mysql/order-by-rand/
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(*))
, donc vous ne voudrez peut-être pas l'utiliser contre 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
Dans MSSQL (testé le 11.0.5569) en utilisant
est nettement plus rapide que
la source
Dans SQL Server, vous pouvez combiner TABLESAMPLE avec NEWID () pour obtenir un assez bon caractère aléatoire et toujours avoir de la vitesse. Ceci est particulièrement utile si vous ne voulez vraiment que 1 ou un petit nombre de lignes.
la source
Avec SQL Server 2012+, vous pouvez utiliser la requête OFFSET FETCH pour le faire pour une seule ligne aléatoire
où id est une colonne d'identité et n est la ligne que vous voulez - calculée comme un nombre aléatoire entre 0 et count () - 1 de la table (le décalage 0 est la première ligne après tout)
Cela fonctionne avec des trous dans les données de la table, tant que vous disposez d'un index avec lequel travailler pour la clause ORDER BY. C'est aussi très bon pour le caractère aléatoire - car vous travaillez vous-même pour passer, mais les inconvénients des autres méthodes ne sont pas présents. De plus, les performances sont assez bonnes, sur un ensemble de données plus petit, elles résistent bien, même si je n'ai pas essayé de tests de performances sérieux contre plusieurs millions de lignes.
la source
la source
ORDER BY RAND()
était mauvaise ...Je dois être d'accord avec CD-MaN: L'utilisation de "ORDER BY RAND ()" fonctionnera bien pour les petites tables ou lorsque vous effectuez votre SELECT seulement quelques fois.
J'utilise également la technique "num_value> = RAND () * ...", et si je veux vraiment avoir des résultats aléatoires, j'ai une colonne "aléatoire" spéciale dans le tableau que je mets à jour une fois par jour environ. Cette exécution unique de MISE À JOUR prendra un certain temps (en particulier parce que vous devrez avoir un index sur cette colonne), mais c'est beaucoup plus rapide que de créer des nombres aléatoires pour chaque ligne à chaque exécution de la sélection.
la source
Soyez prudent car TableSample ne retourne pas réellement un échantillon aléatoire de lignes. Il dirige votre requête pour regarder un échantillon aléatoire des pages de 8 Ko qui composent votre ligne. Ensuite, votre requête est exécutée par rapport aux données contenues dans ces pages. En raison de la façon dont les données peuvent être regroupées sur ces pages (ordre d'insertion, etc.), cela pourrait conduire à des données qui ne sont pas réellement un échantillon aléatoire.
Voir: http://www.mssqltips.com/tip.asp?tip=1308
Cette page MSDN pour TableSample comprend un exemple de la façon de générer un échantillon de données réellement aléatoire.
http://msdn.microsoft.com/en-us/library/ms189108.aspx
la source
Il semble que bon nombre des idées énumérées utilisent encore la commande
Cependant, si vous utilisez une table temporaire, vous pouvez attribuer un index aléatoire (comme la plupart des solutions l'ont suggéré), puis récupérer le premier qui est supérieur à un nombre arbitraire entre 0 et 1.
Par exemple (pour DB2):
la source
Un moyen simple et efficace de http://akinas.com/pages/en/blog/mysql_random_row/
la source
Il existe une meilleure solution pour Oracle au lieu d'utiliser dbms_random.value, alors qu'elle nécessite une analyse complète pour ordonner les lignes par dbms_random.value et elle est assez lente pour les grandes tables.
Utilisez-le à la place:
la source
Pour Firebird:
la source
Pour SQL Server 2005 et supérieur, étendre la réponse de @ GreyPanther aux cas où
num_value
n'a pas de valeurs continues. Cela fonctionne aussi pour les cas où nous n'avons pas réparti les ensembles de données de manière égale et quand ilnum_value
ne s'agit pas d'un nombre mais d'un identifiant unique.la source
Une fonction aléatoire du sql pourrait aider. De plus, si vous souhaitez limiter à une seule ligne, ajoutez simplement cela à la fin.
la source