web-dev-qa-db-fra.com

Sous-requête à l'intérieur d'un Select vs rejoindre

Souvent, je dois retourner le compte de certaines rangées dans une requête qui va chercher d'autres rangées non liées.

Par exemple, un utilisateur de table une table de table et une image de table

User:
id
nickname

Review:
id
to_user_id
from_user_id
rating

Picture:
id:
user_id
url

Disons que je veux que je veux ne seule requête Récupérez le surnom d'un UserID "donné" Toutes ses images URL ainsi que le nombre de personnes ont examiné cet utilisateur.

Le premier et facile de façon que je pense quand cette requête serait-elle:

SELECT
  u.nickname
  (SELECT count(*) FROM review WHERE to_user_id = u.id) as reviewCount,
  p.url
FROM user
LEFT JOIN picture ON p.user_id = u.id
WHERE 
  u.id = 1

L'autre moyen de faire cela est sans ce sous-sélection et en rejoignant la table d'examen à droite user_id

SELECT 
 u.nickname,
 r.reviewCount,
 p.url
FROM user u 
LEFT JOIN (
    SELECT to_user_id, count(*) reviewCount FROM review GROUP BY to_user_id
 ) r ON r.to_user_id = u.id
LEFT JOIN picture ON p.user_id = u.id 
WHERE u.id = 1;

Je ne suis pas un expert avec la performance de la requête DB et le réglage. Quelqu'un pourrait-il m'expliquer si une solution est meilleure que l'autre? (Ou s'il y a une autre meilleure solution)?

Edit : Désolé j'ai oublié de mentionner. Je travaille avec le dernier mysql

3
Alexis

Vous ne spécifiez pas quel SDBM avec lequel vous travaillez. La plupart de ce que j'écris ici devrait être assez indépendant, mais je dispose surtout d'expérience dans MySQL, donc peut-être que différents systèmes permettent d'autres optimisations.

Le (SELECT count(*) FROM review WHERE to_user_id = u.id) as reviewCount est une sous-requête dépendante - Il sera exécuté pour chaque ligne de vos résultats. Même si une exécution est rapide, des milliers de produits de manière potancielle peuvent le rendre lent.

Celui de l'JOIN est une table dérivée - il ne sera exécuté qu'une fois et matérialisé dans une table temporaire, qui sera ensuite jointe à vos autres tables. Si la requête est rapide (peut utiliser l'index sur (to_user_id)), c'est bon. Mais dans ce cas, le nombre sera compté même pour les utilisateurs qui ne montrent pas vraiment dans les résultats. Mais .. vous pouvez simplement pousser la condition là-bas (to_user_id = 1 au lieu du groupe par).

Mais pour rendre les choses si simples, il existe des optimisations de nouvelles versions. La sous-requête dépendante peut être faite plus rapide en utilisant un cache de sous-requête dans Mariadb 10 (et IIRC MySQL 5.7, mais je n'ai pas vérifié). Cela signifie que dans votre cas, toutes les lignes en résultat ont u.id = 1 -> to_user_id = 1 Et la sous-requête ne sera exécutée qu'une fois, puis les résultats mis en cache seront utilisés. Si cela est disponible, la différence entre les deux versions sera minimale.

Personnellement, je préfère votre deuxième version la plupart du temps, mais il y a des cas lorsque le premier sera plus rapide - j'ai une fois une requête où il n'était pas simplement possible de limiter les rangées dans la sous-requête jointe de la bonne voie, mais passant à la Subquier à charge Seule seulement quelques combinaisons uniques ont été réellement lues.

5
jkavalik