web-dev-qa-db-fra.com

sélection rapide d'une ligne dans 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 m'intéresse à toute solution, même si c'est dans une autre langue.

43
lajos

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 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 id 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 de tels trous auront plus de chances d'être sélectionnés que ceux qui suivent les autres identifiants.

Si vous commandez par ordre aléatoire, vous allez avoir une table-scan terrible sur 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 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;
36
Vinko Vrsalovic

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).

30
CesarB

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 qu'elles ne commencent à 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;
12
Bill Karwin

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 lacunes.

3
davr

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 provoqué par ORDER BY RANDOM().

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
3
Rob

Pour sélectionner plusieurs lignes aléatoires dans une table donnée (par exemple, "mots"), notre équipe a imaginé 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
1
Belogradchik Rocks

si vous ne supprimez pas une 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
1
parm.95

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

SELECT * from TABLE order by Rand() LIMIT 1
1
Vinko Vrsalovic

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).

1
Anders Sandvig

Afin de rechercher des lignes aléatoires dans une table, n’utilisez pas ORDER BY Rand () car cela oblige MySQL à effectuer un tri complet des fichiers et à extraire ensuite le nombre de lignes limité requis. Afin d'é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/

1
Sagi Bron

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

1
Ilan Hazan

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
?>

Veuillez garder à l'esprit 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.

0
RandomGuest

Le classique "SELECT id FROM 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é, au lieu de trier le résultat complet.  

0
igelkott

J'ai utilisé cela et le travail a été fait La référence de ici

SELECT * FROM myTable WHERE Rand()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY Rand() LIMIT 30;
0
Yousef Altaf

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 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:

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

Répondre 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;
0
Toni Almeida

Jetez un coup d'œil à ce lien par Jan Kneschke ou ce SO réponse car ils abordent tous les deux la même question. La réponse SO reprend é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 retrouve 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

0
Dipin

J'ai rencontré le problème où mes identifiants n'étaient pas séquentiels. Qu'est-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, c'est vous assurer de renvoyer un résultat suffisant. C'est pourquoi je l'ai défini sur 100. Le fait d'avoir une clause WHERE discount <.2 dans la sous-requête était 10 fois plus lent, il est donc préférable de renvoyer plus de résultats et de limites.

0
bikedorkseattle

Je vois ici beaucoup de solution. 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, pas besoin d'être séquentiel. Il peut s'agir de n'importe quelle colonne clé primaire/unique/incrémentation automatique. Veuillez vous référer à la manière suivante Le moyen le plus rapide de sélectionner une ligne aléatoire dans un grand tableau MySQL

Merci Zillur - www.techinfobest.com

0
Zillur

Avec une commande, vous obtiendrez une table d'analyse complète. Il est préférable d'effectuer un décompte sélectif (*) puis d'obtenir une ligne aléatoire = rownum entre 0 et le dernier registre.

0
MazarD

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
0
MANOJ

Je suis un peu nouveau en SQL mais que diriez-vous de générer un nombre aléatoire dans 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 () dans 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 le SELECT * FROM the_table est lent:
Création d'un nombre aléatoire basé sur mysql_num_rows(), puis déplacement du pointeur de données sur ce point mysql_data_seek(). À quel point cela sera-t-il lent sur de grandes tables comportant, disons, un million de lignes?

0
Adde