J'ai lu quelques informations sur quelques alternatives à la fonction ORDER BY Rand()
de MySQL, mais la plupart des alternatives ne s'appliquent qu'à l'endroit où un seul résultat aléatoire est nécessaire.
Quelqu'un a-t-il une idée de la façon d'optimiser une requête qui renvoie plusieurs résultats aléatoires, tels que:
SELECT u.id,
p.photo
FROM users u, profiles p
WHERE p.memberid = u.id
AND p.photo != ''
AND (u.ownership=1 OR u.stamp=1)
ORDER BY Rand()
LIMIT 18
Cette solution fonctionne mieux avec une colonne indexée .
Voici un exemple simple de banc de requête optimisé portant 100 000 lignes.
OPTIMISE: 300ms
SELECT
g.*
FROM
table g
JOIN
(SELECT
id
FROM
table
WHERE
Rand() < (SELECT
((4 / COUNT(*)) * 10)
FROM
table)
ORDER BY Rand()
LIMIT 4) AS z ON z.id= g.id
remarque sur la quantité maximale : limite 4 et 4/compte (*). Les 4 doivent être le même nombre. Changer le nombre que vous retournez n'influe pas beaucoup sur la vitesse. L'indice de référence à la limite 4 et à la limite 1000 est identique. Limite de 10 000 prises jusqu'à 600ms
note sur la jointure : La randomisation de l'id est plus rapide que la randomisation d'une ligne entière. Puisqu'il doit copier toute la ligne dans la mémoire, randomisez-le. La jointure peut être n'importe quelle table liée à la sous-requête Its pour empêcher les analyses de table.
note where clause : Le nombre where limite le nombre de résultats randomisés. Il faut un pourcentage des résultats et les trie plutôt que la table entière.
note sous-requête : Les conditions si vous faites des jointures et des clauses extra où vous devez les placer à la fois dans la sous-requête et dans la sous-requête. Pour avoir un compte précis et récupérer des données correctes.
NON UTILISÉ: 1200ms
SELECT
g.*
FROM
table g
ORDER BY Rand()
LIMIT 4
AVANTAGES
4x plus rapide que order by Rand()
. Cette solution peut fonctionner avec n'importe quelle table avec une colonne indexée.
LES INCONV&EACUTE;NIENTS
C'est un peu complexe avec des requêtes complexes. Besoin de maintenir 2 bases de code dans les sous-requêtes
Voici une alternative, mais elle repose toujours sur l'utilisation de Rand ():
SELECT u.id,
p.photo,
ROUND(Rand() * x.m_id) 'Rand_ind'
FROM users u,
profiles p,
(SELECT MAX(t.id) 'm_id'
FROM USERS t) x
WHERE p.memberid = u.id
AND p.photo != ''
AND (u.ownership=1 OR u.stamp=1)
ORDER BY Rand_ind
LIMIT 18
Ceci est légèrement plus complexe, mais donne une meilleure distribution des valeurs random_ind:
SELECT u.id,
p.photo,
FLOOR(1 + Rand() * x.m_id) 'Rand_ind'
FROM users u,
profiles p,
(SELECT MAX(t.id) - 1 'm_id'
FROM USERS t) x
WHERE p.memberid = u.id
AND p.photo != ''
AND (u.ownership=1 OR u.stamp=1)
ORDER BY Rand_ind
LIMIT 18
Ce n’est pas le chemin le plus rapide, mais le plus rapide puis le plus commun ORDER BY Rand()
:
ORDER BY Rand()
n'est pas si lent lorsque vous l'utilisez pour rechercher uniquement la colonne indexée. Vous pouvez prendre tous vos identifiants dans une requête comme ceci:
SELECT id
FROM testTable
ORDER BY Rand();
pour obtenir une séquence d'identifiants aléatoires et JOIN
le résultat d'une autre requête avec d'autres paramètres SELECT ou WHERE:
SELECT t.*
FROM testTable t
JOIN
(SELECT id
FROM `testTable`
ORDER BY Rand()) AS z ON z.id= t.id
WHERE t.isVisible = 1
LIMIT 100;
dans votre cas ce serait:
SELECT u.id, p.photo
FROM users u, profiles p
JOIN
(SELECT id
FROM users
ORDER BY Rand()) AS z ON z.id = u.id
WHERE p.memberid = u.id
AND p.photo != ''
AND (u.ownership=1 OR u.stamp=1)
LIMIT 18
C'est une méthode très directe et cela peut ne pas convenir avec de très grandes tables, mais c'est quand même plus rapide que Rand()
. J'ai obtenu un temps d'exécution 20 fois plus rapide en recherchant 3 000 lignes aléatoires sur près de 400 000.
Créez une colonne ou joignez-vous à une sélection avec des nombres aléatoires (générés par exemple en php) et triez-la à l'aide de cette colonne.
Je me suis heurté à cela aujourd'hui et j'essayais d'utiliser «DISTINCT» avec JOIN, mais je supposais qu'il y avait des doublons, car Rand faisait en sorte que chaque ligne JOIN soit distincte. Je me suis un peu embrouillé et j'ai trouvé une solution qui fonctionne, comme ceci:
SELECT DISTINCT t.id,
t.photo
FROM (SELECT u.id,
p.photo,
Rand() as Rand
FROM users u, profiles p
WHERE p.memberid = u.id
AND p.photo != ''
AND (u.ownership=1 OR u.stamp=1)
ORDER BY Rand) t
LIMIT 18
Order by Rand()
est très lent sur les grandes tables,
J'ai trouvé la solution suivante dans un script php:
Select min(id) as min, max(id) as max from table;
Puis faites au hasard en php
$Rand = Rand($min, $max);
Ensuite
'Select * from table where id>'.$Rand.' limit 1';
Semble être assez rapide ....
La solution que j'utilise est également affichée dans le lien ci-dessous: Comment puis-je optimiser la fonction ORDER BY Rand () de MySQL?
Je suppose que votre table d'utilisateurs sera plus grande que votre table de profils, sinon c'est une cardinalité de 1 à 1.
Si c'est le cas, je commencerais par faire une sélection aléatoire sur la table utilisateur avant de rejoindre la table de profil.
D'abord faire la sélection:
SELECT *
FROM users
WHERE users.ownership = 1 OR users.stamp = 1
Ensuite, à partir de ce pool, sélectionnez des lignes aléatoires en fonction de la probabilité calculée. Si votre table comporte M lignes et que vous souhaitez sélectionner N lignes aléatoires, la probabilité de sélection aléatoire doit être N/M. Par conséquent:
SELECT *
FROM
(
SELECT *
FROM users
WHERE users.ownership = 1 OR users.stamp = 1
) as U
WHERE
Rand() <= $limitCount / (SELECT count(*) FROM users WHERE users.ownership = 1 OR users.stamp = 1)
Où N est $ limitCount et M est la sous-requête qui calcule le nombre de lignes de la table. Cependant, comme nous travaillons sur la probabilité, il est possible que LESS soit inférieur à $ limitCount. Par conséquent, nous devrions multiplier N par un facteur pour augmenter la taille du pool aléatoire.
c'est à dire:
SELECT*
FROM
(
SELECT *
FROM users
WHERE users.ownership = 1 OR users.stamp = 1
) as U
WHERE
Rand() <= $limitCount * $factor / (SELECT count(*) FROM users WHERE users.ownership = 1 OR users.stamp = 1)
Je règle généralement $ factor = 2. Vous pouvez définir le facteur sur une valeur inférieure pour réduire davantage la taille du pool aléatoire (par exemple 1,5).
À ce stade, nous aurions déjà limité une table de taille M à une taille d'environ 2N. De là, nous pouvons faire un JOIN puis LIMIT.
SELECT *
FROM
(
SELECT *
FROM
(
SELECT *
FROM users
WHERE users.ownership = 1 OR users.stamp = 1
) as U
WHERE
Rand() <= $limitCount * $factor / (SELECT count(*) FROM users WHERE users.ownership = 1 OR users.stamp = 1)
) as randUser
JOIN profiles
ON randUser.id = profiles.memberid AND profiles.photo != ''
LIMIT $limitCount
Sur une grande table, cette requête surperformera une requête ORDER by Rand () normale.
J'espère que cela t'aides!