sélection rapide d’une ligne aléatoire à partir d’une grande table dans mysql

Quel est un moyen rapide de sélectionner une ligne aléatoire dans une grande table mysql?

Je travaille en php, mais je suis intéressé par toute solution, même si c’est dans une autre langue.

Prenez tous les identifiants, choisissez-en un au hasard et récupérez la ligne complète.

Si vous savez que les identifiants sont séquentiels sans trous, vous pouvez simplement saisir le max et calculer un identifiant aléatoire.

S’il y a des trous ici et là mais surtout des valeurs séquentielles, et que vous ne vous souciez pas d’un hasard légèrement asymésortingque, prenez la valeur maximale, calculez un identifiant et sélectionnez la première ligne avec un identifiant égal ou supérieur à celui que vous avez calculé. La raison de l’inclinaison est que les identifiants qui suivent ces trous auront plus de chances d’être sélectionnés que ceux qui suivent un autre identifiant.

Si vous commandez par hasard, vous allez avoir un terrible scan de table entre vos mains, et le mot rapide ne s’applique pas à une telle solution.

Ne faites pas cela, et vous ne devriez pas commander par un GUID, il a le même problème.

Je savais qu’il devait y avoir un moyen de le faire en une seule requête rapidement. Et la voici:

Un moyen rapide sans implication de code externe, bravo à

http://jan.kneschke.de/projects/mysql/order-by-rand/

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; 

MediaWiki utilise un truc intéressant (pour la fonctionnalité spéciale de Wikipédia: aléatoire): la table contenant les articles a une colonne supplémentaire avec un nombre aléatoire (généré lors de la création de l’article). Pour obtenir un article aléatoire, générez un nombre aléatoire et récupérez l’article avec la valeur suivante plus grande ou plus petite (ne vous en souvenez pas) dans la colonne des nombres aléatoires. Avec un index, cela peut être très rapide. (Et MediaWiki est écrit en PHP et développé pour MySQL.)

Cette approche peut poser problème si les nombres résultants sont mal dissortingbués; IIRC, cela a été corrigé sur MediaWiki, donc si vous décidez de le faire de cette façon, vous devriez regarder le code pour voir comment cela se fait actuellement (probablement ils régénèrent périodiquement la colonne des nombres aléatoires).

Voici une solution qui fonctionne assez rapidement, et qui obtient une meilleure dissortingbution aléatoire sans dépendre de la contiguïté des valeurs d’identification ou du fait qu’elle commence à 1.

 SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*) FROM mytable))); SET @sql := CONCAT('SELECT * FROM mytable LIMIT ', @r, ', 1'); PREPARE stmt1 FROM @sql; EXECUTE stmt1; 

Peut-être que vous pourriez faire quelque chose comme:

 SELECT * FROM table WHERE id= (FLOOR(RAND() * (SELECT COUNT(*) FROM table) ) ); 

Cela suppose que vos numéros d’identification sont tous séquentiels, sans aucun écart.

Ajoutez une colonne contenant une valeur aléatoire calculée à chaque ligne et utilisez-la dans la clause de classement, en limitant à un résultat lors de la sélection. Cela fonctionne plus rapidement que d’avoir la table scan que ORDER BY RANDOM() provoque.

Mise à jour: vous devez toujours calculer une valeur aléatoire avant d’émettre l’ SELECT lors de la récupération, bien sûr, par exemple

 SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1 

Un moyen facile mais lent serait (bon pour les petites tables)

 SELECT * from TABLE order by RAND() LIMIT 1 

En pseudo-code:

 sql "select id from table" store result in list n = random(size of list) sql "select * from table where id=" + list[n] 

Cela suppose que id est une clé unique (primaire).

Il existe un autre moyen de produire des lignes aléatoires en utilisant uniquement une requête et sans ordre de rand (). Cela implique des variables définies par l’utilisateur. Voir comment produire des lignes aléatoires à partir d’une table

Afin de trouver des lignes aléatoires à partir d’une table, n’utilisez pas ORDER BY RAND () car cela oblige MySQL à effectuer un sorting de fichiers complet et ensuite seulement pour récupérer le nombre de lignes limite requirejs. Pour éviter ce type de fichier complet, utilisez la fonction RAND () uniquement à la clause where. Il s’arrêtera dès qu’il atteindra le nombre de lignes requirejs. Voir http://www.rndblog.com/how-to-select-random-rows-in-mysql/

Si vous ne supprimez pas la ligne dans cette table, le moyen le plus efficace est:

(si vous connaissez le mininum id, ignorez-le)

 SELECT MIN(id) AS minId, MAX(id) AS maxId FROM table WHERE 1 $randId=mt_rand((int)$row['minId'], (int)$row['maxId']); SELECT id,name,... FROM table WHERE id=$randId LIMIT 1 

Pour sélectionner plusieurs lignes aléatoires dans une table donnée (par exemple, des mots), notre équipe a trouvé cette beauté:

 SELECT * FROM `words` AS r1 JOIN (SELECT MAX(`WordID`) as wid_c FROM `words`) as tmp1 WHERE r1.WordID >= (SELECT (RAND() * tmp1.wid_c) AS id) LIMIT n 

Le classique “SELECT id FROM table ORDER BY RAND () LIMIT 1” est en fait correct.

Voir l’extrait suivant du manuel MySQL:

Si vous utilisez LIMIT row_count avec ORDER BY, MySQL met fin au sorting dès qu’il a trouvé les premières lignes row_count du résultat sortingé, plutôt que de sortinger le résultat entier.

Avec une commande, vous ferez un tableau d’parsing complet. C’est mieux si vous faites un compte de sélection (*) et obtenez plus tard une ligne aléatoire = rownum entre 0 et le dernier registre

Jetez un oeil à ce lien par Jan Kneschke ou cette réponse SO alors qu’ils discutent tous deux de la même question. La réponse SO couvre également différentes options et propose de bonnes suggestions en fonction de vos besoins. Jan passe en revue toutes les différentes options et caractéristiques de performance de chacun. Il se termine avec la méthode la plus optimisée pour faire cela dans une sélection MySQL:

 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; 

HTH,

-Dipin

Je suis un peu nouveau à SQL mais que diriez-vous de générer un nombre aléatoire en PHP et en utilisant

 SELECT * FROM the_table WHERE primary_key >= $randNr 

Cela ne résout pas le problème avec des trous dans la table.

Mais voici une suggestion de lassevks:

 SELECT primary_key FROM the_table 

Utilisez mysql_num_rows () en PHP pour créer un nombre aléatoire basé sur le résultat ci-dessus:

 SELECT * FROM the_table WHERE primary_key = rand_number 

Sur une note, remarquez à quel point SELECT * FROM the_table est lent:
Créer un nombre aléatoire basé sur mysql_num_rows() puis déplacer le pointeur de données vers ce point mysql_data_seek() . À quel point cela sera-t-il lent sur les grandes tables avec un million de lignes?

J’ai rencontré le problème où mes identifiants n’étaient pas séquentiels. Ce que j’ai trouvé avec ça

 SELECT * FROM products WHERE RAND()<=(5/(SELECT COUNT(*) FROM products)) LIMIT 1 

Les lignes renvoyées sont environ 5, mais je le limite à 1.

Si vous voulez append une autre clause WHERE, cela devient un peu plus intéressant. Disons que vous voulez rechercher des produits à rabais.

 SELECT * FROM products WHERE RAND()<=(100/(SELECT COUNT(*) FROM pt_products)) AND discount<.2 LIMIT 1 

Ce que vous avez à faire est de vous assurer que vous retournez suffisamment de résultat, c'est pourquoi je l'ai défini à 100. Avoir une clause de réduction <

Je vois ici beaucoup de solution. Un ou deux semble bien, mais d’autres solutions ont des contraintes. Mais la solution suivante fonctionnera pour toutes les situations

 select a.* from random_data a, (select max(id)*rand() randid from random_data) b where a.id >= b.randid limit 1; 

Ici, id, pas besoin d’être séquentiel. Il peut s’agir de n’importe quelle colonne d’incrémentation clé unique / unique / auto. S’il vous plaît voir le moyen le plus rapide suivant pour sélectionner une ligne aléatoire d’une grande table MySQL

Merci Zillur – http://www.techinfobest.com

Utilisez la requête ci-dessous pour obtenir la ligne aléatoire

 SELECT user_firstname , COUNT(DISTINCT usr_fk_id) cnt FROM userdetails GROUP BY usr_fk_id ORDER BY cnt ASC LIMIT 1 

Dans mon cas, ma table a un identifiant comme clé primaire, auto-incrémenté sans écart, donc je peux utiliser COUNT(*) ou MAX(id) pour obtenir le nombre de lignes.

J’ai fait ce script pour tester l’opération la plus rapide:

 logTime(); query("SELECT COUNT(id) FROM tbl"); logTime(); query("SELECT MAX(id) FROM tbl"); logTime(); query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1"); logTime(); 

Les résultats sont les suivants:

  • Count: 36.8418693542479 ms
  • Max: 0.241041183472 ms
  • Commande : 0.216960906982 ms

Répondez avec la méthode de commande:

 SELECT FLOOR(RAND() * ( SELECT id FROM tbl ORDER BY id DESC LIMIT 1 )) n FROM tbl LIMIT 1 ... SELECT * FROM tbl WHERE id = $result; 

J’ai utilisé ceci et le travail a été fait la référence d’ ici

 SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30; 

Créez une fonction pour faire ceci probablement la meilleure réponse et la réponse la plus rapide ici!

Avantages – Fonctionne même avec Gaps et extrêmement rapide.

 0){ return $fetch_$data; }else{ rando('','',$max); // Start Over the results returned nothing } }else{ if($max != '0'){ $irand = rand(0,$max); rando('s1',$irand,$max); // Start rando with new random ID to fetch }else{ $query = mysqli_query($sqlConnect, "SELECT `id` FROM `yourtable` ORDER BY `id` DESC LIMIT 0,1"); $fetched_data = mysqli_fetch_assoc($query); $max = $fetched_data['id']; $irand = rand(1,$max); rando('s1',$irand,$max); // Runs rando against the random ID we have selected if data exist will return } } } $your_data = rando(); // Returns listing data for a random entry as a ASSOC ARRAY ?> 

Gardez à l’esprit que ce code n’a pas été testé, mais qu’il s’agit d’un concept fonctionnel pour renvoyer des entrées aléatoires, même avec des lacunes.

Méthode rapide et sale:

 SET @COUNTER=SELECT COUNT(*) FROM your_table; SELECT PrimaryKey FROM your_table LIMIT 1 OFFSET (RAND() * @COUNTER); 

La complexité de la première requête est O (1) pour les tables MyISAM.

La deuxième requête accompagne une parsing complète de la table. Complexité = O (n)

Méthode sale et rapide:

Conservez une table séparée à cet effet uniquement. Vous devez également insérer les mêmes lignes dans cette table lors de l’insertion dans la table d’origine. Hypothèse: Pas de suppressions.

 CREATE TABLE Aux( MyPK INT AUTO_INCREMENT, PrimaryKey INT ); SET @MaxPK = (SELECT MAX(MyPK) FROM Aux); SET @RandPK = CAST(RANDOM() * @MaxPK, INT) SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK = @RandPK); 

Si DELETE est autorisé,

 SET @delta = CAST(@RandPK/10, INT); SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta LIMIT 1); 

La complexité globale est O (1).

SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;