Question

Comment puis-je demander une ligne au hasard (ou presque aléatoires, comme cela est possible) en pur SQL?

Était-ce utile?

La solution

Voir ce post: SQL pour Sélectionner une ligne au hasard à partir d'une table de base de données.Il va par le biais de 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électionner une ligne au hasard avec MySQL:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

Sélectionner une ligne au hasard avec PostgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Sélectionner une ligne au hasard avec Microsoft SQL Server:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

Sélectionner une ligne au hasard avec IBM DB2

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Sélectionnez un enregistrement aléatoire avec Oracle:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

Autres conseils

Des Solutions comme Jeremies:

SELECT * FROM table ORDER BY RAND() LIMIT 1

de travail, mais ils ont besoin d'un balayage séquentiel de la table (parce que le hasard de la valeur associée à chaque ligne doit être calculé de sorte que le plus petit peut être déterminé), qui peut être assez lente pour que même les petits tableaux.Ma recommandation serait d'utiliser une sorte d'numériques indexés colonne (nombre de tables ont comme leurs clés primaires), et puis écrire quelque chose comme:

SELECT * FROM table WHERE num_value >= RAND() * 
    ( SELECT MAX (num_value ) FROM table ) 
ORDER BY num_value LIMIT 1

Cela fonctionne en temps logarithmique, indépendamment de la taille de la table, si num_value il est indexé.Une mise en garde:cela suppose que num_value est également réparties dans la gamme 0..MAX(num_value).Si votre dataset fortement s'écarte de cette hypothèse, vous obtiendrez des résultats faussés (certaines lignes apparaissent plus souvent que d'autres).

Je ne sais pas comment efficace, c'est, mais je l'ai déjà utilisé:

SELECT TOP 1 * FROM MyTable ORDER BY newid()

Parce que les Guid sont assez aléatoire, la commande signifie que vous obtenez une ligne au hasard.

ORDER BY NEWID()

prend 7.4 milliseconds

WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)

prend 0.0065 milliseconds!

Je vais certainement aller avec la méthode de ce dernier.

Vous n'avez pas dit quel serveur que vous utilisez.Dans les anciennes versions de SQL Server, vous pouvez utiliser ceci:

select top 1 * from mytable order by newid()

Dans SQL Server 2005, vous pouvez utiliser TABLESAMPLE pour obtenir un échantillon aléatoire c'est répétitif:

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;

Pour SQL Server

newid()/de la commande par fonctionner, mais il sera très coûteux pour les grands ensembles de résultats, car il doit générer un id pour chaque ligne, puis de les trier.

TABLESAMPLE() est bonne d'un point de vue des performances, mais vous aurez l'agrégation de résultats (toutes les lignes sur une page sera retourné).

Pour une meilleure exécution de vrai aléatoire de l'échantillon, le meilleur moyen est de filtrer les lignes au hasard.J'ai trouvé l'exemple de code suivant dans la documentation en Ligne SQL Server de l'article Limiter les Ensembles de Résultats en Utilisant TABLESAMPLE:

Si vous voulez vraiment un échantillon aléatoire de des lignes individuelles, de modifier votre requête filtrer les lignes au hasard, au lieu de à l'aide de TABLESAMPLE.Par exemple, l' requête ci-dessous utilise la fonction NEWID la fonction de retour d'environ un pourcentage de lignes de la De vente.SalesOrderDetail:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

La colonne SalesOrderID est inclus dans la somme de contrôle de l'expression de sorte que NEWID() évalue une fois par ligne à réaliser l'échantillonnage d'un par ligne.L'expression CAST(somme de contrôle(NEWID(), SalesOrderID) & 0x7fffffff en tant QUE float / CAST (0x7fffffff COMME int) évalue à un random float valeur entre 0 et 1.

Lorsqu'il est exécuté sur une table avec 1 000 000 de 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 l'aide de TABLESAMPLE, il vous donnera les meilleures performances.Sinon, utiliser la fonction newid()/méthode de filtrage.newid()/de la commande par devrait être le dernier recours, si vous avez un grand ensemble de résultats.

Si possible, utiliser des informations d'états pour éviter l'inefficacité de ces deux indices sur RND() et la création d'un nombre record de terrain.

PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?,1";
SET @n=FLOOR(RAND()*(SELECT COUNT(*) FROM table));
EXECUTE RandomRecord USING @n;

La meilleure façon est de mettre une valeur aléatoire dans une nouvelle colonne dans ce but, et d'utiliser quelque chose comme ceci (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 y a certains préjugés à l'encontre des valeurs plus petites, mais ils ont trouvé que c'était suffisante pour envelopper la valeur aléatoire autour de zéro quand n lignes sont extraites.

newid() solution peut exiger un full table scan de sorte que chaque ligne peut être affecté à un nouveau guid, qui sera beaucoup moins performant.

rand() la solution peut ne pas fonctionner du tout (c'est à direavec MSSQL) parce que la fonction sera évalué juste une fois, et chaque la ligne sera attribué le même "aléatoire" nombre.

Pour SQL Server 2005 et 2008, si nous voulons un échantillon aléatoire de lignes individuelles (à partir de Livres En Ligne):

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

Insted d' à l'aide de RAND(), comme il n'est pas encouragée, vous pouvez simplement obtenir un max ID (=Max.):

SELECT MAX(ID) FROM TABLE;

obtenez un hasard entre 1..Max (=My_Generated_Random)

My_Generated_Random = rand_in_your_programming_lang_function(1..Max);

et puis exécutez ce SQL:

SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1

Notez qu'il va vérifier pour les lignes les Id qui sont SUPÉRIEURE ou ÉGALE à la valeur choisie.Il est également possible de chasser pour la ligne vers le bas dans le tableau, et obtenir une égale ou inférieure ID de la My_Generated_Random, puis modifier la requête comme ceci:

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1

Comme l'a souligné @BillKarwin commentaire de @cnu de la réponse...

Lors de la combinaison avec une LIMITE, j'ai trouvé qu'il effectue beaucoup mieux (au moins avec PostgreSQL 9.1) à se JOINDRE à un hasard de la commande plutôt que d'ordonner 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 >= 1349928000 AND ...
ORDER BY r.rand
LIMIT 100

Assurez-vous que le 'r' génère 'rand' valeur pour chaque valeur de clé dans la requête complexe qui est joint avec elle, mais encore limiter le nombre de lignes de 'r' si possible.

Le CAST Entier est particulièrement utile pour PostgreSQL 9.2 qui a tri spécifique d'optimisation pour l'entier et unique de précision les types flottants.

La plupart des solutions ici visent à éviter le tri, mais ils doivent encore faire un balayage séquentiel sur une table.

Il est aussi un moyen d'éviter le balayage séquentiel par la commutation de l'analyse d'index.Si vous connaissez la valeur de l'index de votre ligne au hasard, vous pouvez obtenir le résultat presque instantially.Le problème est comment deviner la valeur de l'indice.

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;

J'solution ci-dessus vous devinez 10 divers aléatoire des valeurs de l'indice de la gamme 0 ..[dernière valeur de l'id].

Le nombre 10 est arbitraire, que vous pouvez utiliser 100 ou 1000, comme il (étonnamment) n'ont pas un grand impact sur le temps de réponse.

Il y a aussi un problème - si vous avez éparses id vous risquez de manquer.La solution est de avoir un plan de sauvegarde :) Dans ce cas, une pure ancien ordre aléatoire() de la requête.Lorsqu'il est combiné id 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))
    union all (select * from cms_refs order by random() limit 1)
    limit 1;

Pas la union TOUS la clause.Dans ce cas, si la première partie renvoie toutes les données de la seconde n'est JAMAIS exécutée!

À la fin, mais arrivé ici via Google, donc pour le bien de la postérité, je vais ajouter une solution de rechange.

Une autre approche consiste à utiliser deux fois, avec une alternance de commandes.Je ne sais pas si c'est de la "pure" SQL", car il utilise une variable dans le TOP, mais il fonctionne dans SQL Server 2008.Voici un exemple, je suis contre l'utilisation d'un tableau de mots du dictionnaire, si je veux un mot au hasard.

SELECT TOP 1
  word
FROM (
  SELECT TOP(@idx)
    word 
  FROM
    dbo.DictionaryAbridged WITH(NOLOCK)
  ORDER BY
    word DESC
) AS D
ORDER BY
  word ASC

Bien sûr, @idx est certains généré de façon aléatoire entier qui varie de 1 à COUNT(*) sur la table cible, inclusivement.Si votre colonne est indexée, vous bénéficierez de trop.Un autre avantage est que vous pouvez l'utiliser dans une fonction NEWID() n'est pas autorisé.

Enfin, la requête ci-dessus s'exécute en environ 1/10 de l'exec temps d'un NEWID()-type de requête sur la même table.YYMV.

Vous pouvez aussi essayer d'utiliser new id() fonction.

Suffit d'écrire votre requête et d'utiliser la commande par new id() fonction.C'est assez aléatoire.

Pour MySQL, pour obtenir l'enregistrement aléatoire

 SELECT name
  FROM random AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

Plus de détails http://jan.kneschke.de/projects/mysql/order-by-rand/

N'arrive pas à voir cette variation dans les réponses à ce jour.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:

Minimum exemple:

select top 10 percent *
from table_name
order by rand(checksum(*))

Normalisé temps d'exécution:1.00

NewId() exemple:

select top 10 percent *
from table_name
order by newid()

Normalisé temps d'exécution:1.02

NewId() est pas significativement plus lent que rand(checksum(*)), de sorte que vous pouvez ne pas vouloir l'utiliser contre les grands ensembles d'enregistrements.

La sélection Initiale de la Graine:

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.

Dans MSSQL (testé sur 11.0.5569) à l'aide de

SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)

est nettement plus rapide que

SELECT TOP 100 * FROM employee ORDER BY NEWID()

Dans SQL Server, vous pouvez combiner TABLESAMPLE avec NEWID() pour obtenir assez bon caractère aléatoire et ont encore de la vitesse.Ceci est particulièrement utile si vous ne voulez 1, ou un petit nombre de lignes.

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()
 SELECT * FROM table ORDER BY RAND() LIMIT 1

Je suis d'accord avec CD-MaN:À l'aide de "ORDER BY RAND()" fonctionnent bien pour les petites tables ou lorsque vous faites vos SÉLECTIONNER seulement quelques fois.

J'utilise aussi le "num_value >= RAND() * ..." technique", et si je veux vraiment avoir des résultats aléatoires, j'ai un spécial "aléatoire" de la colonne dans la table que j'ai mise à jour une fois par jour ou plus.Que la seule mise à JOUR va prendre un certain temps (en particulier parce que vous aurez d'avoir un index sur cette colonne), mais c'est beaucoup plus rapide que de créer des nombres aléatoires pour chaque ligne à chaque fois que l'instruction select est exécutée.

Être prudent, car TableSample ne fait pas de retour d'un échantillon aléatoire de lignes.Il dirige la requête oeil à un échantillon aléatoire de la 8KO pages qui composent votre ligne.Ensuite, votre requête est exécutée sur les données contenues dans ces pages.En raison de la façon dont les données peuvent être regroupées sur ces pages (à l'ordre d'insertion, etc), ce qui pourrait conduire à des données qui n'est 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 actualy échantillon aléatoire de données.

http://msdn.microsoft.com/en-us/library/ms189108.aspx

Il semble que de nombreuses idées toujours utiliser la commande

Toutefois, si vous utilisez une table temporaire, vous êtes en mesure d'attribuer un hasard index (comme beaucoup de solutions ont suggéré), puis prenez la première qui est supérieur à un nombre arbitraire entre 0 et 1.

Par exemple (pour DB2):

WITH TEMP AS (
SELECT COMLUMN, RAND() AS IDX FROM TABLE)
SELECT COLUMN FROM TABLE WHERE IDX > .5
FETCH FIRST 1 ROW ONLY

Un moyen simple et efficace de http://akinas.com/pages/en/blog/mysql_random_row/

SET @i = (SELECT FLOOR(RAND() * COUNT(*)) FROM table); PREPARE get_stmt FROM 'SELECT * FROM table LIMIT ?, 1'; EXECUTE get_stmt USING @i;

Il y a une meilleure solution pour Oracle au lieu d'utiliser dbms_random.valeur, bien qu'il exige un scan complet à l'ordre des lignes dbms_random.valeur et il est assez lent pour les grandes tables.

Utilisez ceci à la place:

SELECT *
FROM employee sample(1)
WHERE rownum=1

Pour Firebird:

Select FIRST 1 column from table ORDER BY RAND()

Avec SQL Server 2012+ vous pouvez utiliser la DÉCALAGE CHERCHER requête pour ce faire, pour une seule ligne au hasard

select  * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS ONLY

où id est une colonne d'identité, et n est le rang que vous voulez - calculé comme un nombre aléatoire entre 0 et count()-1 du tableau (décalage de 0 est sur la première ligne, après tout)

Cela fonctionne avec des trous dans le tableau de données, aussi longtemps que vous avez un indice à travailler avec la clause ORDER BY.Il est aussi très bon pour le caractère aléatoire de l' - vous le travail que vous-même pour passer en mais les remarques à faire dans d'autres méthodes ne sont pas présents.En outre, la performance est assez bonne, sur un dataset plus petit, il tient bien, même si je n'ai pas essayé de sérieux tests de performance à l'encontre de plusieurs millions de lignes.

Pour SQL Server 2005 et au-dessus, l'extension @GreyPanther de réponse pour le cas où num_value n'a pas de valeurs continues.Cela fonctionne aussi pour les cas où nous n'avons pas uniformément distribué des ensembles de données et lors de la num_value n'est pas un nombre mais un identifiant unique.

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)

Fonction aléatoire à partir de sql pourrait aider.Aussi, si vous souhaitez limiter à une seule ligne, il suffit d'ajouter qu'à la fin.

SELECT column FROM table
ORDER BY RAND()
LIMIT 1
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top