90 votes

Comment puis-je optimiser MySQL ' s ordre par RAND() fonction ?

J'aimerais optimiser mes requêtes, je regarde mysql-slow.log.

La plupart de mes requêtes lentes contient ORDER BY RAND(). Je ne peux pas trouver une vraie solution pour résoudre ce problème. Theres est une solution possible à MySQLPerformanceBlog mais je ne pense pas que cela soit suffisant. Sur mal optimisé (ou fréquemment mis à jour, l'utilisateur géré) tables il ne fonctionne pas ou j'ai besoin d'exécuter deux ou plusieurs requêtes avant que je puisse choisir mon PHP-aléatoires générés ligne.

Est-il une solution pour ce problème?

Merci!

Un mannequin exemple:

SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation, accomodation_category
WHERE   accomodation.ac_status != 'draft'
        AND accomodation.ac_category = accomodation_category.acat_id
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
ORDER BY
        RAND()
LIMIT 1

67voto

Quassnoi Points 191041

Essayez ceci:

SELECT  *
FROM    (
        SELECT  @cnt := COUNT(*) + 1,
                @lim := 10
        FROM    t_random
        ) vars
STRAIGHT_JOIN
        (
        SELECT  r.*,
                @lim := @lim - 1
        FROM    t_random r
        WHERE   (@cnt := @cnt - 1)
                AND RAND(20090301) < @lim / @cnt
        ) i

Ceci est particulièrement efficace sur MyISAM (depuis l' COUNT(*) est instantané), mais même dans InnoDB c'est 10 fois plus efficace que l' ORDER BY RAND().

L'idée principale ici est que nous n'avons pas de tri, mais au lieu de les garder deux variables et de calculer le running probability d'une ligne à être sélectionné à l'étape actuelle.

Voir cet article dans mon blog pour plus de détails:

Mise à jour:

Si vous avez besoin de sélectionner, mais un seul enregistrement aléatoire, essayez ceci:

SELECT  aco.*
FROM    (
        SELECT  minid + FLOOR((maxid - minid) * RAND()) AS randid
        FROM    (
                SELECT  MAX(ac_id) AS maxid, MIN(ac_id) AS minid
                FROM    accomodation
                ) q
        ) q2
JOIN    accomodation aco
ON      aco.ac_id =
        COALESCE
        (
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_id > randid
                AND ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        ),
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        )
        )

Cela suppose que votre ac_ids'sont répartis de manière plus ou moins égale.

12voto

DisgruntledGoat Points 21368

Il dépend du hasard que vous devez être. La solution que vous avez des liens entre les œuvres assez bien de l'OMI. Sauf si vous avez de grandes lacunes dans le champ ID, il est encore assez aléatoire.

Toutefois, vous devriez être en mesure de le faire en une seule requête à l'aide de ce (pour la sélection d'une seule valeur):

SELECT [fields] FROM [table] WHERE id >= FLOOR(RAND()*MAX(id)) LIMIT 1

D'autres solutions:

  • Ajouter un permanent float champ appelé random le tableau et le remplir avec des nombres aléatoires. Vous pouvez ensuite générer un nombre aléatoire en PHP et n' "SELECT ... WHERE rnd > $random"
  • Saisir la totalité de la liste d'Identifiants et de les mettre en cache dans un fichier texte. Lire le fichier et choisir un IDENTIFIANT aléatoire.
  • Mettre en Cache les résultats de la requête au format HTML et de les garder pendant quelques heures.

1voto

Bill Karwin Points 204877

Voici comment je le ferais :

1voto

TheJacobTaylor Points 2982

Oserais-je demander si la requête est en fait levier index ? Je ne suis pas sûr de l’efficacité de la méthode de MySQL RAND est, mais certaines versions de MySQL sont très friands de groupes électrogènes de résultats volumineux sur disque et ensuite choisir l’un. Si vous pouvez garder votre requête isolée à un index, vous pourriez avoir exponentiellement accélérer les performances. (selon la taille de l’index, performance de RAND,...)

Pouvez-vous poster un plan pour la requête d’expliquer ?

Jacob

0voto

Karl Mikko Points 9

Cela vous donnera une requête unique sup qui utilisera l’index pour obtenir un id aléatoire, puis l’autre requête tirera obtenir votre table jointe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X