2009-07-01 9 views
0

J'ai une requête simple (PostGreSQL si cela importe) que tous les éléments récupère pour un_utilisateur sauf ceux qu'elle a sur sa liste:requête lente avec jointure externe gauche et est la condition null

select i.* 
from core_item i 
left outer join core_item_in_basket b on (i.id=b.item_id and b.user_id=__some_user__) 
where b.on_wishlist is null; 

La requête ci-dessus s'exécute en ~ 50000ms (oui, le nombre est correct). Si je supprime la condition "b.on_wishlist is null" ou la rende "b.on_wishlist n'est pas nulle", la requête s'exécutera en 50 ms (un changement assez important).

La requête a plus de jointures et de conditions, mais ce n'est pas pertinent car seul celui-ci le ralentit.

Quelques informations sur la taille de la base de données:

  • core_items a ~ 10.000 dossiers
  • core_user a ~ 5.000 dossiers
  • core_item_in_basket a ~ 2.000
  • records (dont environ 50% a on_wishlist = true, le reste est null)

Je n'ai aucun index (sauf pour les IDs et les clés étrangères) sur ces deux tables.

La question est: que dois-je faire pour que cette exécution plus rapide? J'ai moi-même quelques idées à découvrir ce soir, mais j'aimerais aussi que vous aidiez, si possible.

Merci!

+0

peut-être que vous pourriez regarder l'exécution des plans pour voir quel est le problème. En théorie, les requêtes 'nul' ou 'non nul' doivent avoir le même plan d'exécution si le solde est de 50/50. Les statistiques statiques pour la table doivent être fausses! –

+0

@ Scorpi0 pas tout à fait. Rappelez-vous que ceci est une jointure externe. Les statistiques nulles pour core_item_in_basket sont en effet 50/50, mais la requête essaie de faire correspondre chaque enregistrement de core_item avec un enregistrement de core_item_in_basket, produisant beaucoup de nulls supplémentaires sur le côté droit de la jointure. – michuk

+0

Deux types de ligne correspondent à la condition "où b.on_wishlist est nulle" 1. Rows dans core_item sans les correspondants dans core_item_in_basket; 2. Les lignes dans core_item qui ont des correspondants dans core_item_in_basket, mais certaines de ces correspondances correspondent à on_wishlist est nulle Voulez-vous les deux groupes de lignes? –

Répondre

2

Désolé pour l'ajout de la 2ème réponse, mais stackoverflow ne me permet pas de formater les commentaires correctement, et comme le formatage est essentiel, je dois poster une réponse.

Couple d'options:

  1. INDEX q ON CREER core_item_in_basket (user_id, item_id) OÙ on_wishlist est nulle;
  2. même index, mais changez l'ordre des colonnes. SELECT i. * FROM core_item i Où i.id pas dans (sélectionnez item_id FROM core_item_in_basket WHERE on_wishlist est null AND user_id = __some_user__); (cette requête peut bénéficier de l'index du point n ° 1, mais ne bénéficiera pas de l'index n ° 2.
  3. SELECT * from core_item où id in (sélectionnez id from core_item SAUF sélectionnez item_id FROM core_item_in_basket WHERE on_wishlist est null AND user_id = __some_user__) ;

faites-nous savoir les résultats :)

+0

La réponse numéro 4 est correcte. Cela a beaucoup de sens quand on y pense. Nous ne voulons pas vraiment avoir une jointure externe (pour quoi faire?), Excluez simplement les quelques éléments qui sont sur la wishlist. Les index ne sont pas bons ici car le problème n'était pas la table core_item_in_basket (qui est minuscule) mais la jointure inutile. Tout vérifié et travaillant déjà dans la production, consultez http://filmaster.com Et merci beaucoup depesz pour sauver mes fesses encore: P – michuk

+0

BTW, la nouvelle explication analyser est ici si vous êtes intéressé: http : //explain.depesz.com/s/sIG – michuk

5

essayer d'utiliser existe pas:

select i.* 
from core_item i 
where not exists (select * from core_item_in_basket b where i.id=b.item_id and b.user_id=__some_user__) 
+0

Mais cela néglige la condition "b.on_wishlist is null" qui semble être le facteur de ralentissement ici. –

+0

ah alors il suffit d'ajouter cela à l'endroit où existe. aussi dans votre jointure essayez d'ajouter que c'est null dans la jointure sur la condition –

+0

Non, celui-ci n'aide pas non plus. PostgreSQL a appris à optimiser automatiquement les choses comme ça depuis des années. – michuk

1

Avez-vous essayé d'ajouter un indice sur on_wishlist?

Il semble que cette colonne doit être vérifiée pour chaque ligne de la requête. Si vos tables sont très volumineuses, cela peut avoir un impact significatif sur la vitesse de la requête. Lorsque vous définissez la condition on_wishlist dans la clause where, ce qui entraîne l'évaluation (en fonction de ce que décide le planificateur de requête) après l'exécution de la jointure, cette comparaison doit être effectuée pour chaque ligne résultante potentielle de la jointure Les tables core_items et core_item_in_basket sont assez volumineuses et vous n'avez pas d'index pour cette colonne. L'optimiseur de requêtes a donc très peu à faire, ce qui entraîne probablement un temps de requête excessif.

La taille de core_user ne devrait avoir aucune influence (car elle n'est pas référencée dans la requête).

+0

Non, les index sur wishlist ont peu de sens car ils ne peuvent être que vrais ou nuls. – michuk

2

Vous pourriez vouloir en savoir plus sur le but de cette requête - comme certaines techniques le font et d'autres n'ont pas de sens, selon le cas d'utilisation.

À quelle fréquence l'utilisez-vous?

Est-il exécuté pour seulement 1 utilisateur, ou vous l'exécutez pour tous les utilisateurs dans une sorte de boucle?Ne: expliquez analyser et mettre la sortie sur explain.depesz.com afin que vous puissiez voir pourquoi il est si lent.

+0

@depesz Bien sûr, je le ferai dans 2h quand je rentrerai à la maison car je déteste écrire des requêtes sur mon mobile via ssh. En note, je voulais vous poser directement des questions à propos de celui-ci, mais j'ai décidé de ne pas vous déranger avec tous les problèmes PSQL que j'ai :) – michuk

+0

Donc, répondre à vos questions: a) Le but de la requête est d'obtenir X prochains films à évaluer, mais en excluant ceux qui sont sur la liste de souhaits de quelqu'un. b) Il fonctionne pour un utilisateur chaque fois que l'utilisateur veut évaluer un film aléatoire ici http://filmaster.com/rate-movies/ - bien que nous limitons les résultats à 10, les mettre en cache et exécuter la même requête après l'évaluation 10 prochains films c) La requête réelle (celle que j'ai montrée ci-dessus est une version simplifiée) ainsi que l'explication de l'analyse est ici: http://explain.depesz.com/s/Cf d) Et voici la même requête sans la condition de liste de souhaits: http://explain.depesz.com/s/BPd – michuk

Questions connexes