Réponses:
Un excellent poste de traitement de plusieurs cas, du simple, aux lacunes, au non uniforme avec des lacunes.
http://jan.kneschke.de/projects/mysql/order-by-rand/
Pour le cas le plus général, voici comment procéder:
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
Cela suppose que la distribution des identifiants est égale et qu'il peut y avoir des lacunes dans la liste des identifiants. Voir l'article pour des exemples plus avancés
mysqli_fetch_assoc($result)
? Ou ces 10 résultats ne se distinguent-ils pas nécessairement?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
Pas la solution efficace mais fonctionne
ORDER BY RAND()
est relativement lent
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
prend 0,0010, sans LIMIT 10 il a fallu 0,0012 (dans ce tableau 3500 mots).
Requête simple qui a d' excellentes performances et fonctionne avec des lacunes :
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
Cette requête sur une table de 200 Ko prend 0,08 s et la version normale (SELECT * FROM tbl ORDER BY RAND () LIMIT 10) prend 0,35 s sur ma machine.
Ceci est rapide car la phase de tri utilise uniquement la colonne ID indexée. Vous pouvez voir ce comportement dans l'explication:
SÉLECTIONNEZ * À PARTIR DE L'ORDRE TBL PAR RAND () LIMITE 10:
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND () LIMIT 10) as t2 ON t1.id = t2.id
Version pondérée : https://stackoverflow.com/a/41577458/893432
Je reçois des requêtes rapides (environ 0,5 seconde) avec un processeur lent , sélectionnant 10 lignes aléatoires dans une base de données MySQL de 400 Ko sans mise en cache de 2 Go. Voir ici mon code: sélection rapide de lignes aléatoires dans MySQL
<?php
$time= microtime_float();
$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);
$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
if($id_in) $id_in.=",$id";
else $id_in="$id";
}
mysql_free_result($rquery);
$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
logger("$id, $url",1);
}
mysql_free_result($rquery);
$time= microtime_float()-$time;
logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);
?>
ORDER BY RAND()
FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';
pour le voir.
ORDER BY RAND()
car il trie uniquement les identifiants (pas les lignes complètes), donc la table temporaire est plus petite, mais doit toujours les trier tous.
Sa requête très simple et sur une seule ligne.
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
est très lent si la table est grande
Du livre:
Choisissez une ligne aléatoire en utilisant un décalage
Une autre technique encore qui évite les problèmes rencontrés dans les alternatives précédentes consiste à compter les lignes de l'ensemble de données et à renvoyer un nombre aléatoire entre 0 et le nombre. Utilisez ensuite ce nombre comme décalage lors de l'interrogation de l'ensemble de données
<?php
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();
Utilisez cette solution lorsque vous ne pouvez pas assumer des valeurs de clé contiguës et que vous devez vous assurer que chaque ligne a une chance égale d'être sélectionnée.
SELECT count(*)
devient lent.
Comment sélectionner des lignes aléatoires dans une table:
D'ici: Sélectionnez des lignes aléatoires dans MySQL
Une amélioration rapide par rapport au "scan de table" consiste à utiliser l'index pour récupérer des identifiants aléatoires.
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
PRIMARY KEY
).
Eh bien, si vous n'avez pas de lacunes dans vos clés et qu'elles sont toutes numériques, vous pouvez calculer des nombres aléatoires et sélectionner ces lignes. mais ce ne sera probablement pas le cas.
Une solution serait donc la suivante:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
ce qui garantira essentiellement que vous obtenez un nombre aléatoire dans la plage de vos clés, puis vous sélectionnez le meilleur suivant qui est le plus grand. vous devez le faire 10 fois.
cependant, ce n'est PAS vraiment aléatoire, car vos clés ne seront probablement pas distribuées également.
C'est vraiment un gros problème et pas facile à résoudre en remplissant toutes les exigences, rand () de MySQL est le meilleur que vous pouvez obtenir si vous voulez vraiment 10 lignes aléatoires.
Il existe cependant une autre solution qui est rapide mais qui a aussi un compromis en matière de hasard, mais qui peut vous convenir mieux. Lisez à ce sujet ici: Comment puis-je optimiser la fonction ORDER BY RAND () de MySQL?
La question est de savoir à quel point vous en avez besoin.
Pouvez-vous expliquer un peu plus afin que je puisse vous donner une bonne solution.
Par exemple, une entreprise avec laquelle je travaillais avait une solution où ils avaient besoin d'une rapidité absolue extrêmement rapide. Ils ont fini par pré-remplir la base de données avec des valeurs aléatoires qui ont été sélectionnées en ordre décroissant et réglées à nouveau sur différentes valeurs aléatoires par la suite.
Si vous ne mettez presque jamais à jour, vous pouvez également remplir un identifiant d'incrémentation afin que vous n'ayez pas d'espace et que vous puissiez simplement calculer des clés aléatoires avant de sélectionner ... Cela dépend du cas d'utilisation!
Id
et toutes vos requêtes aléatoires vous le rendront Id
.
FLOOR(RAND()*MAX(id))
est biaisé pour renvoyer des identifiants plus importants.
J'avais besoin d'une requête pour renvoyer un grand nombre de lignes aléatoires à partir d'une table assez grande. C'est ce que j'ai trouvé. Obtenez d'abord l'ID d'enregistrement maximal:
SELECT MAX(id) FROM table_name;
Remplacez ensuite cette valeur par:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
Où max est l'ID d'enregistrement maximum dans la table et n est le nombre de lignes que vous souhaitez dans votre jeu de résultats. L'hypothèse est qu'il n'y a pas de lacunes dans les identifiants d'enregistrement, bien que je doute que cela affecterait le résultat s'il y en avait (je ne l'ai pas essayé cependant). J'ai également créé cette procédure stockée pour être plus générique; transmettez le nom de la table et le nombre de lignes à renvoyer. J'exécute MySQL 5.5.38 sur Windows 2008, 32 Go, double 3GHz E5450, et sur une table avec 17361 264 lignes, il est assez cohérent à ~ 0,03 sec / ~ 11 sec pour renvoyer 1000000 de lignes. (les heures proviennent de MySQL Workbench 6.1; vous pouvez également utiliser CEIL au lieu de FLOOR dans la 2e instruction select selon vos préférences)
DELIMITER $$
USE [schema name] $$
DROP PROCEDURE IF EXISTS `random_rows` $$
CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN
SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @t = CONCAT(
'SELECT * FROM ',
tab_name,
' WHERE id>FLOOR(RAND()*@max) LIMIT ',
num_rows);
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
puis
CALL [schema name].random_rows([table name], n);
Toutes les meilleures réponses ont déjà été postées (principalement celles faisant référence au lien http://jan.kneschke.de/projects/mysql/order-by-rand/ ).
Je veux identifier une autre possibilité d'accélération - la mise en cache . Réfléchissez à la raison pour laquelle vous devez obtenir des lignes aléatoires. Vous souhaitez probablement afficher une publication aléatoire ou une annonce aléatoire sur un site Web. Si vous obtenez 100 req / s, est-il vraiment nécessaire que chaque visiteur reçoive des lignes aléatoires? Habituellement, il est tout à fait correct de mettre en cache ces X lignes aléatoires pendant 1 seconde (ou même 10 secondes). Peu importe si 100 visiteurs uniques dans la même 1 seconde reçoivent les mêmes messages aléatoires, car la seconde suivante, 100 autres visiteurs recevront un ensemble de messages différent.
Lorsque vous utilisez cette mise en cache, vous pouvez également utiliser une partie de la solution la plus lente pour obtenir les données aléatoires car elles ne seront récupérées de MySQL qu'une seule fois par seconde, indépendamment de votre demande.
J'ai amélioré la réponse de @Riedsio. C'est la requête la plus efficace que je puisse trouver sur une grande table uniformément distribuée avec des lacunes (testée sur l'obtention de 1000 lignes aléatoires à partir d'une table qui a> 2,6 milliards de lignes).
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
Permettez-moi de déballer ce qui se passe.
@max := (SELECT MAX(id) FROM table)
MAX(id)
chaque fois que vous avez besoin d'une ligneSELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
Faire l'union vous aide à tout ranger dans 1 requête afin que vous puissiez éviter de faire plusieurs requêtes. Il vous permet également d'économiser les frais généraux de calcul MAX(id)
. Selon votre application, cela peut avoir beaucoup ou très peu d'importance.
Notez que cela n'obtient que les identifiants et les obtient dans un ordre aléatoire. Si vous voulez faire quelque chose de plus avancé, je vous recommande de faire ceci:
SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id
LIMIT 1
de LIMIT 30
partout dans la requête
LIMIT 1
vous LIMIT 30
donnerait 30 enregistrements consécutifs à partir d'un point aléatoire dans le tableau. Vous devriez plutôt avoir 30 copies de la (SELECT id FROM ....
partie au milieu.
Riedsio
répondre. J'ai essayé avec 500 accès par seconde à la page en utilisant PHP 7.0.22 et MariaDB sur centos 7, avec la Riedsio
réponse j'ai obtenu plus de 500 réponses supplémentaires puis votre réponse.
J'ai utilisé ce http://jan.kneschke.de/projects/mysql/order-by-rand/ publié par Riedsio (j'ai utilisé le cas d'une procédure stockée qui renvoie une ou plusieurs valeurs aléatoires):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
INSERT INTO rands
SELECT r1.id
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;
SET cnt = cnt - 1;
END LOOP loop_me;
Dans l'article, il résout le problème des lacunes dans les identifiants provoquant des résultats pas si aléatoires en maintenant une table (en utilisant des déclencheurs, etc ... voir l'article); Je résout le problème en ajoutant une autre colonne à la table, remplie de nombres contigus, à partir de 1 ( modifier: cette colonne est ajoutée à la table temporaire créée par la sous-requête lors de l'exécution, n'affecte pas votre table permanente):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET @no_gaps_id := 0;
INSERT INTO rands
SELECT r1.id
FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
(SELECT (RAND() *
(SELECT COUNT(*)
FROM random)) AS id)
AS r2
WHERE r1.no_gaps_id >= r2.id
ORDER BY r1.no_gaps_id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
Dans l'article, je peux voir qu'il s'est donné beaucoup de mal pour optimiser le code; Je n'ai aucune idée si / à quel point mes changements ont un impact sur les performances mais fonctionnent très bien pour moi.
@no_gaps_id
aucun index ne peut être utilisé, donc si vous recherchez EXPLAIN
votre requête, vous avez Using filesort
et Using where
(sans index) pour les sous-requêtes, contrairement à la requête d'origine.
Voici un changeur de jeu qui peut être utile pour beaucoup;
J'ai une table avec 200k lignes, avec des identifiants séquentiels , j'avais besoin de choisir N lignes aléatoires, alors j'ai choisi de générer des valeurs aléatoires basées sur le plus grand ID de la table, j'ai créé ce script pour savoir quelle est 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:
36.8418693542479
ms0.241041183472
ms0.216960906982
msSur la base de ces résultats, l'ordre desc est l'opération la plus rapide pour obtenir l'id max,
voici ma réponse à la question:
SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM (
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 10) a
...
SELECT * FROM tbl WHERE id IN ($result);
FYI: Pour obtenir 10 lignes aléatoires d'une table de 200k, cela m'a pris 1,78 ms (y compris toutes les opérations du côté php)
LIMIT
légèrement - vous pouvez obtenir des doublons.
C'est super rapide et 100% aléatoire même si vous avez des lacunes.
x
de lignes dont vous disposezSELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
entre 0 etx
SELECT * FROM TABLE LIMIT 1 offset a_i
pour i = 1, ..., 10J'ai trouvé ce hack dans le livre SQL Antipatterns de Bill Karwin .
SELECT column FROM table ORDER BY RAND() LIMIT 10
est en O (nlog (n)). Alors oui, c'est la solution à jeun et elle fonctionne pour toute distribution d'ID.
x
. Je dirais que ce n'est pas une génération aléatoire de 10 lignes. Dans ma réponse, vous devez exécuter la requête à l'étape trois 10 fois, c'est-à-dire que l'on n'obtient qu'une ligne par exécution et ne vous inquiétez pas si le décalage est à la fin du tableau.
Combinez la réponse de @redsio avec une table temporaire (600K n'est pas tant que ça):
DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO tmp_randorder (data_id) select id from datatable;
Et puis prenez une version de @redsios Answer:
SELECT dt.*
FROM
(SELECT (RAND() *
(SELECT MAX(id)
FROM tmp_randorder)) AS id)
AS rnd
INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
INNER JOIN datatable AS dt on dt.id = rndo.data_id
ORDER BY abs(rndo.id - rnd.id)
LIMIT 1;
Si la table est grande, vous pouvez tamiser la première partie:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
Version: vous pouvez conserver la table tmp_randorder
persistante, appelez-la datatable_idlist. Recréez ce tableau à certains intervalles (jour, heure), car il obtiendra également des trous. Si votre table devient vraiment grande, vous pouvez également remplir des trous
sélectionnez l.data_id dans son ensemble dans datatable_idlist l jointure gauche datatable dt on dt.id = l.data_id où dt.id est nul;
Version: donnez à votre jeu de données une colonne random_sortorder directement dans datatable ou dans une table supplémentaire persistante datatable_sortorder
. Indexez cette colonne. Générez une valeur aléatoire dans votre application (je l'appellerai $rand
).
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
Cette solution discrimine les «lignes de bord» avec le plus haut et le plus bas random_sortorder, afin de les réorganiser à intervalles (une fois par jour).
Une autre solution simple serait de classer les lignes et d'en récupérer une au hasard et avec cette solution, vous n'aurez pas besoin d'avoir de colonne basée sur 'Id' dans le tableau.
SELECT d.* FROM (
SELECT t.*, @rownum := @rownum + 1 AS rank
FROM mytable AS t,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM mytable))) AS n
) d WHERE rank >= @cnt LIMIT 10;
Vous pouvez modifier la valeur limite selon votre besoin d'accéder à autant de lignes que vous le souhaitez, mais il s'agit principalement de valeurs consécutives.
Cependant, si vous ne voulez pas de valeurs aléatoires consécutives, vous pouvez extraire un échantillon plus grand et le sélectionner de manière aléatoire. quelque chose comme ...
SELECT * FROM (
SELECT d.* FROM (
SELECT c.*, @rownum := @rownum + 1 AS rank
FROM buildbrain.`commits` AS c,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM buildbrain.`commits`))) AS rnd
) d
WHERE rank >= @cnt LIMIT 10000
) t ORDER BY RAND() LIMIT 10;
Une façon que je trouve assez bonne s'il y a un identifiant généré automatiquement est d'utiliser l'opérateur modulo '%'. Par exemple, si vous avez besoin de 10 000 enregistrements aléatoires sur 70 000, vous pouvez simplifier cela en disant que vous avez besoin d'une ligne sur 7. Cela peut être simplifié dans cette requête:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
Si le résultat de la division des lignes cibles par le total disponible n'est pas un entier, vous aurez quelques lignes supplémentaires par rapport à ce que vous avez demandé, vous devez donc ajouter une clause LIMIT pour vous aider à réduire le jeu de résultats comme ceci:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
Cela nécessite une analyse complète, mais c'est plus rapide que ORDER BY RAND, et à mon avis plus simple à comprendre que les autres options mentionnées dans ce fil. De plus, si le système qui écrit dans la base de données crée des ensembles de lignes par lots, vous pourriez ne pas obtenir un résultat aussi aléatoire que prévu.
Si vous voulez un enregistrement aléatoire (peu importe s'il y a des lacunes entre les identifiants):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
J'ai parcouru toutes les réponses, et je pense que personne ne mentionne cette possibilité du tout, et je ne sais pas pourquoi.
Si vous voulez une simplicité et une vitesse maximales, à moindre coût, alors il me semble logique de stocker un nombre aléatoire contre chaque ligne de la base de données. Créez simplement une colonne supplémentaire random_number
et définissez sa valeur par défaut sur RAND()
. Créez un index sur cette colonne.
Ensuite, lorsque vous souhaitez récupérer une ligne, générez un nombre aléatoire dans votre code (PHP, Perl, peu importe) et comparez-le à la colonne.
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
Je suppose que même si c'est très soigné pour une seule rangée, pour dix rangées comme l'OP vous a demandé de l'appeler dix fois (ou de proposer un ajustement intelligent qui m'échappe immédiatement)
Les éléments suivants doivent être rapides, impartiaux et indépendants de la colonne id. Cependant, cela ne garantit pas que le nombre de lignes renvoyées correspondra au nombre de lignes demandées.
SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)
Explication: en supposant que vous vouliez 10 lignes sur 100, chaque ligne a 1/10 de probabilité d'être sélectionné, ce qui pourrait être réalisé par WHERE RAND() < 0.1
. Cette approche ne garantit pas 10 lignes; mais si la requête est exécutée suffisamment de fois, le nombre moyen de lignes par exécution sera d'environ 10 et chaque ligne du tableau sera sélectionnée de manière égale.
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Vous pouvez également appliquer une clause where comme so
PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Testé sur 600 000 lignes (700 Mo), l'exécution des requêtes de table a pris environ 0,016 s de disque dur
--EDIT--
Le décalage peut prendre une valeur proche de la fin de la table, ce qui entraînera l'instruction select renvoyant moins de lignes (ou peut-être seulement 1 ligne), pour éviter cela, nous pouvons vérifier à offset
nouveau après l'avoir déclaré, comme si
SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;
J'utilise cette requête:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
temps de requête: 0,016 s
Voici comment je le fais:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
Je l'aime car il ne nécessite pas d'autres tables, il est simple à écrire et très rapide à exécuter.
Utilisez la requête simple ci-dessous pour obtenir des données aléatoires à partir d'une table.
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
Je suppose que c'est la meilleure façon possible ..
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no