2009-11-05 7 views
1

J'ai lu que ce n'est pas une bonne idée d'exécuter des requêtes SQL avec ORDER BY RAND() sur de grandes bases de données.Random Records Mysql PHP

Alors, voici ma chance de casser le code. Le code doit sélectionner 10 identifiants aléatoires dans la base de données, puis sélectionner une seconde pour saisir les lignes aléatoires.

$sql = "SELECT id FROM table WHERE image != '' 
     ORDER BY id DESC LIMIT 50;"; 

$result = mysql_query($sql); 


while($row = mysql_fetch_array($result)) 
{ 
    foreach($row as $key => $value) 
    { 
     $array[] = $value; 
    } 
} 
$rand_keys = array_rand($array, 10); 

foreach($rand_keys as $value) 
{ 

    $rand_arr[] = $array[$value]; 

} 
$rand_list = implode("," , $rand_arr); 

$sql = "SELECT image FROM table 
     WHERE image != '' 
     AND id IN ($rand_list)"; 
$result = mysql_query($sql); 

Des suggestions pour accélérer ou simplifier?

Répondre

2

Cinq Quatre choses:

  1. Pourquoi si vous avez seulement besoin de 12 vous 50 id récupérez s? (Vous choisissez 12 identifiants parmi les 50 derniers - ce qui est logique, bien que ce ne soit pas particulièrement aléatoire au sens général du terme - est-ce intentionnellement le sous-ensemble de vos lignes que vous voulez sélectionner?).

  2. Avez-vous profilé l'instruction SQL ORDER BY RAND() pour voir si elle est lente pour vous? Quelle est la taille de votre ensemble de données?

  3. Vous n'avez pas besoin de WHERE image != '' dans la dernière requête, puisque vous avez déjà sélectionné uniquement id s pour lesquels image != ''. Pourquoi faites-vous array_rand($array, 10) - vous dites que vous voulez 12 valeurs?

  4. Vous pouvez simplifier la cueillette des valeurs aléatoires comme ceci:

$rand_arr = array_rand(array_flip($array), 12);

+0

J'éviterais vraiment 'order by rand()' comme une habitude générale. J'ai vu cela grind occupé tables MyISAM de seulement quelques centaines de milliers de lignes. –

+0

Allez maintenant Dominic. # 3 et # 4 sont des erreurs typo évidentes/idées négligées. Mon erreur. # 5 était très utile. # 2 est dans mon esprit. Je cours ce code sur un serveur partagé et il a arrêté mon site Web (même effet avec ORDER BY rand() réellement.) Cela m'a incité à repenser complètement les rangées aléatoires, c.-à-d. servant des images aléatoires. – rrrfusco

0

Je suis d'accord avec les points 1 et 2 ci-dessus - si vous pouvez effectuer la sélection des données aléatoires à l'intérieur votre application au même niveau que les données, moins vous aurez besoin d'écrire pour faire la même chose.

0

Il n'y a pas de moyen particulièrement efficace de le faire avec élégance.

Mais vous pouvez le pirater à partir d'un certain nombre de directions. Si votre dataset est de la bonne taille (trop grand pour "order by rand()" mais pas trop gros), possède des valeurs d'identifiant séquentiel, et ne supprime généralement pas beaucoup, vous pouvez toujours faire quelque chose comme:

SELECT MIN(id) as min, MAX(id) as max FROM table 

Générer un certain nombre N d'entiers aléatoires entre "min" et "max" (inclus). Appelons-le 50. Si vous ne supprimez jamais rien de la table, N peut probablement être 12. Si vous supprimez, faites de la serviette arithmatique et trouvez un bon nombre. Vous pouvez probablement errer du bon côté.

SELECT * FROM table WHERE id IN (<your set of integers>) AND image_id = '' LIMIT 12; 

Assurez-vous d'avoir au moins 12 résultats. Sinon, fondamentalement répéter et combiner.

Pour les grands ensembles, cette méthode devrait fonctionner beaucoup mieux que ORDER BY RAND(), surtout si votre séquence d'ID n'est pas très clairsemée.

+0

J'aime cette approche, mais elle ne convient que dans une configuration à un seul maître où auto_increment est monotone: auto_increment_increment, les paramètres auto_increment_offset pourraient les disséminer. –

+0

Bon point. Cela ne fonctionne que lorsque vous commencez à gérer plusieurs maîtres. – timdev

0

Je voudrais mettre l'accent sur le point 5 de Dominic comme un moyen d'impact assez faible pour récupérer des données de manière aléatoire. Vous pouvez aussi sort() cette liste d'identifiants (je crois que MySQL détecte cela et omet de trier cette liste pour vous.

Il existe d'autres techniques pour les grands ensembles de données et les taux de requêtes élevés qui impliquent des vues matérialisées (essentiellement la mise en cache d'une table). Vous essayez de résoudre un problème de performance existant sur une grande table occupée?

0

Une autre option consiste à utiliser une fonction de hachage aléatoire et triée.

Récupérez les identifiants maximum et minimum pour la table, et utilisez la fonction rand() de PHP pour générer un nombre aléatoire entre le maximum et le minimum.

ensuite utiliser ce numéro pour vous des graines fonction de hachage .Assume {sel} dans le SQL signifie que le nombre entier aléatoire généré par PHP

SELECT image FROM table 
WHERE image != '' 
ORDER BY MOD(ABS({salt}-id), MOD({salt}, 10)), ABS({salt}-id)); 

Vous pouvez optimiser un peu l'exécution du MOD ({} sel, 10) calcul en PHP et en passant la valeur dans la requête.

0

Si la taille des lignes n'est pas trop grande, je profilerais juste en sélectionnant 50 lignes et en gardant une liste aléatoire de 12 dans l'application. Oui, cela signifie que vous jetez 80% des lignes sélectionnées. Quand vous parlez 80% de 50, est-ce vraiment un crime? C'est le genre de chose que SQL n'est pas bon.