Question

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 elle est dans une autre langue.

Était-ce utile?

La solution

Prenez tous les identifiants, choisissez-en un au hasard et récupérez la totalité de la ligne.

Si vous savez que les identifiants sont séquentiels sans trous, vous pouvez simplement saisir le maximum 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 tenez pas compte d'un caractère aléatoire légèrement asymétrique, saisissez la valeur maximale, calculez un identifiant et sélectionnez la première ligne avec un identifiant égal ou supérieur à celui vous avez calculé. La raison en est que les identifiants qui suivent de tels trous auront plus de chances d’être pris que ceux qui en suivent un autre.

Si vous commandez par ordre aléatoire, vous obtiendrez un terrible tableau sur les mains et le mot quick ne s'applique pas à une telle solution.

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

Autres conseils

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

Un moyen rapide sans implication de code externe, félicitations à

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 une astuce intéressante (pour Wikipedia: Spécial: fonctionnalité aléatoire): le tableau avec les articles comporte 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 obtenez l'article avec la prochaine valeur plus grande ou plus petite (ne vous souvenez plus laquelle) dans la colonne du nombre aléatoire. 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 obtenus sont mal distribués. IIRC, cela a été corrigé sur MediaWiki, donc si vous décidez de le faire de cette façon, vous devriez jeter un coup d'œil au code pour voir comment cela se passe actuellement (probablement, ils régénèrent périodiquement la colonne de nombre aléatoire).

Voici une solution qui s'exécute assez rapidement et qui obtient une meilleure distribution aléatoire sans que les valeurs id soient contiguës ou à partir de 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 pourriez-vous faire quelque chose comme:

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

Cela suppose que vos identifiants soient tous séquentiels, sans espace.

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

Mise à jour: vous devez toujours calculer une valeur aléatoire avant d'émettre l'instruction SELECT lors de l'extraction, 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 seulement une requête et sans ordre avec rand (). Cela implique des variables définies par l'utilisateur. Voir comment créer des lignes aléatoires à partir d'une table

Afin de rechercher des lignes aléatoires dans une table, n & # 8217; n'utilisez pas ORDER BY RAND () car il oblige MySQL à effectuer un tri complet des fichiers, puis à extraire le nombre de lignes limité requis. Pour éviter ce tri de fichier complet, utilisez la fonction RAND () uniquement dans la clause where. Il s'arrêtera dès qu'il atteindra le nombre de lignes requis. Voir http://www.rndblog.com/how-to- select-random-rows-in-mysql /

si vous ne supprimez pas de ligne dans cette table, le moyen le plus efficace est:

(si vous connaissez l'identifiant mininum, sautez-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 un tableau donné (par exemple, "mots"), notre équipe a créé 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 de la table ORDER BY RAND () LIMIT 1 &"; est en fait OK.

Voir l'extrait suivant du manuel MySQL:

Si vous utilisez LIMIT row_count avec ORDER BY, MySQL termine le tri dès qu'il a trouvé les premières lignes row_count du résultat trié, plutôt que de trier l'intégralité du résultat.

Avec une commande, vous ferez un tableau complet. Il est préférable d’effectuer un compte sélectionné (*) et d’obtenir ultérieurement une ligne aléatoire = rownum entre 0 et le dernier registre

Regardez ce lien de Jan Kneschke ou cette réponse à la présente SO en tant que ils discutent tous les deux de la même question. La réponse à cette question couvre également diverses options et propose de bonnes suggestions en fonction de vos besoins. Jan passe en revue toutes les différentes options et les caractéristiques de performance de chacune. Il se termine avec la méthode suivante la plus optimisée pour le faire dans une sélection de 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 en SQL mais que diriez-vous de générer un nombre aléatoire en PHP et d’utiliser

SELECT * FROM the_table WHERE primary_key >= $randNr

cela ne résout pas le problème de 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

Notons à quel point la lenteur est lente SELECT * FROM the_table:
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 comportant, disons, un million de lignes?

J'ai rencontré le problème où mes identifiants n'étaient pas séquentiels. Ce que je suis venu avec cela.

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 souhaitez ajouter une autre clause WHERE, cela devient un peu plus intéressant. Supposons que vous souhaitiez rechercher des produits en promotion.

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

Ce que vous devez faire est de vous assurer de renvoyer un résultat suffisant. C'est pourquoi je l'ai défini sur 100. Disposer d'une clause WHERE discount < .2 dans la sous-requête était 10x plus lent, il est donc préférable de renvoyer plus de résultats et de limites.

Je vois ici beaucoup de solutions. Une ou deux semblent correctes 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, il n'est pas nécessaire que ce soit séquentiel. Il peut s'agir de n'importe quelle colonne clé primaire / unique / incrémentation automatique. Consultez la suivante Le moyen le plus rapide de sélectionner une ligne aléatoire dans une grande table MySQL

Merci Zillur - 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 en tant que clé primaire, incrémentation automatique sans espace, afin que je puisse utiliser COUNT(*) ou MAX(id) pour obtenir le nombre de lignes.

J'ai créé 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:

  • Compte: 36.8418693542479 ms
  • Max: 0.241041183472 ms
  • Ordre : 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é cela et le travail a été fait La référence à partir de 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 des lacunes et extrêmement rapide.

<?

$sqlConnect = mysqli_connect('localhost','username','password','database');

function rando($data,$find,$max = '0'){
   global $sqlConnect; // Set as mysqli connection variable, fetches variable outside of function set as GLOBAL
   if($data == 's1'){
     $query = mysqli_query($sqlConnect, "SELECT * FROM `yourtable` ORDER BY `id` DESC LIMIT {$find},1");

     $fetched_data = mysqli_fetch_assoc($query);
      if(mysqli_num_rows($fetched_data>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
?>

N'oubliez pas que ce code n'a pas été testé, mais qu'il s'agit d'un concept fonctionnel permettant de renvoyer des entrées aléatoires, même avec des espaces. Tant que les espaces ne sont pas suffisamment importants pour causer un problème de temps de chargement.

Méthode rapide et incorrecte:

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 analyse complète de table. Complexité = O (n)

Méthode rapide et sale:

Conservez une table séparée uniquement à cette fin. Vous devez également insérer les mêmes lignes dans cette table à chaque insertion dans la table d'origine. Hypothèse: pas de suppression.

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 les suppressions sont autorisées,

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;

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top