2011-03-05 4 views
1

Dans mon application Web, j'ai créé un système de messagerie interne. Je souhaite placer un lien "précédent" et un lien "suivant" sur chaque page (où l'utilisateur consulte le message).Optimisation des requêtes ORDER BY LIMIT dans MySQL

Afin d'obtenir le suivant et précédent id J'exercerai deux requêtes:

Pour le précédent:

SELECT id FROM pages WHERE (id<$requestedPageId) ORDER BY id DESC LIMIT 1

Et pour la prochaine:

SELECT id FROM pages WHERE (id>$requestedPageId) ORDER BY id LIMIT 1

EXPLAIN indique que le type de requête est "range" et la colonne rows indique qu'il examinerait toutes les lignes s id plus petit ou plus grand que l'id de la page (un grand nombre). La ligne Extra indique "Using where".

Il semble que MySQL ignore que je ne veux qu'une ligne. MySQL n'est-il pas assez intelligent pour optimiser ce type de requête afin qu'il trouve la ligne de la page et recherche la première ligne correspondante?

Y a-t-il une meilleure approche pour obtenir l'identifiant de la page suivante et précédente?

Notes complémentaires:

  • Ce problème semble exister sur chaque ORDER BY requêtes de type LIMIT (par exemple .: lorsque je partage une longue liste de plusieurs pages.).
  • Lorsque la clause n'est pas si simple (je souhaite laisser l'utilisateur accéder à la page suivante/précédente à laquelle il a accès.)
  • Toutes les colonnes apparaissent dans WHERE sont indexées (id est la clé primaire)
  • les variables sont protégées contre l'injection.

EDIT1:

Ainsi, la requête J'utilise actuellement:

SELECT id 
FROM reports 
WHERE (id<$requestedPageId) AND ((isPublic=1) OR (recipientId=$recipient)) 
ORDER BY id DESC 
LIMIT 1 

Ou quand je re-facteur comme la réponse dit:

SELECT MAX(id) 
FROM reports 
WHERE (id<$requestedPageId) AND ((isPublic=1) OR (recipientId=$recipient)) 

Répondre

3

Pour la précédente

SELECT MAX(id) FROM pages WHERE id<$requestPageId 

Et pour la prochaine

SELECT MIN(id) FROM pages WHERE id>$requestedPageId 
+0

Il optimise les tables dans ce cas simple. Mais dès que je mets une condition supplémentaire à la clause where, il va examiner toutes les lignes. (Le type EXPLAIN sera ALL). Je vais éditer mon post original et j'ai mis dans la requête que j'utilise. – Calmarius

+0

Avez-vous des index mis en place sur isPublic et recipientId? –

1

La base de données se comporte comme prévu. Votre requête est une requête de plage en raison du symbole inférieur (ID < $ requestedPageId). L'instruction OR rend plus difficile l'utilisation d'un index unique pour trouver les résultats. Et, triant les résultats signifie qu'il doit obtenir toutes les lignes correspondantes pour effectuer le tri, même si vous ne voulez que 1 ligne.

Vous ne serez pas en mesure d'en faire une requête de type "const", mais vous pourrez peut-être l'optimiser en utilisant des index, des sous-requêtes et/ou des déclarations de syndicats.

Voici une requête pour toutes les gérer. Je ne dis pas que c'est la meilleure solution, mais juste une façon d'aborder le problème.Pour commencer, cette requête fonctionnera mieux si vous créez deux index, un sur recipientId et un autre sur isPublic.

SELECT 
GREATEST(
    (SELECT MAX(id) FROM reports 
    WHERE id < $requestedPageId AND recipientId = $recipient), 
    (SELECT MAX(id) FROM reports 
    WHERE id < $requestedPageId AND isPublic = 1) 
) AS prev_id 
LEAST(
    (SELECT MIN(id) FROM reports 
    WHERE id > $requestedPageId AND recipientId = $recipient), 
    (SELECT MIN(id) FROM reports 
    WHERE id > $requestedPageId AND isPublic = 1) 
) AS next_id