Est-il préférable de séparer une grande requête en plusieurs requêtes plus petites?

13

Il y a des situations qui nécessitent d'avoir une très grosse requête joignant plusieurs tables avec des instructions de sous-sélection pour produire les résultats souhaités.

Ma question est la suivante: devrions-nous envisager d'utiliser plusieurs requêtes plus petites et intégrer les opérations logiques dans la couche application en interrogeant la base de données en plusieurs appels ou est-il préférable de les avoir toutes en une seule fois?
Par exemple, considérez la requête suivante:

SELECT *
FROM   `users`
WHERE  `user_id` IN (SELECT f2.`friend_user_id`
                     FROM   `friends` AS f1
                            INNER JOIN `friends` AS f2
                              ON f1.`friend_user_id` = f2.`user_id`
                     WHERE  f2.`is_page` = 0
                            AND f1.`user_id` = "%1$d"
                            AND f2.`friend_user_id` != "%1$d"
                            AND f2.`friend_user_id` NOT IN (SELECT `friend_user_id`
                                                            FROM   `friends`
                                                            WHERE  `user_id` = "%1$d"))
       AND `user_id` NOT IN (SELECT `user_id`
                             FROM   `friend_requests`
                             WHERE  `friend_user_id` = "%1$d")
       AND `user_image` IS NOT NULL
ORDER  BY RAND() 
LIMIT %2$d

Quelle est la meilleure façon de procéder?

Hamed Momeni
la source

Réponses:

14

Je vais être en désaccord sur les requêtes volumineuses et compliquées avec datagod ici. Je ne vois ces problèmes que comme des problèmes s'ils sont désorganisés. En termes de performances, celles-ci sont presque toujours meilleures, car le planificateur a beaucoup plus de liberté pour récupérer les informations. Cependant, les requêtes volumineuses doivent être écrites dans un souci de maintenabilité. En général, j'ai trouvé que le SQL simple et bien structuré est facile à déboguer même lorsqu'une seule requête se poursuit pour plus de 200 lignes. En effet, vous avez généralement une assez bonne idée du type de problème auquel vous êtes confronté, il n'y a donc que quelques zones de la requête que vous devez vérifier.

Les problèmes de maintenance, IME, surviennent lorsque la structure de SQL tombe en panne. Les requêtes longues et complexes dans les sous-sélections altèrent la lisibilité et le dépannage, tout comme les vues en ligne, et ces deux éléments doivent être évités dans les requêtes longues. Au lieu de cela, utilisez des VUES si vous le pouvez (notez que si vous êtes sur MySQL, les vues ne fonctionnent pas très bien, mais sur la plupart des autres bases de données, elles le font), et utilisez des expressions de table communes là où celles-ci ne fonctionnent pas (MySQL ne prend pas en charge ces btw).

Les longues requêtes complexes fonctionnent assez bien à la fois dans un cas de maintenabilité et de performances où vous gardez vos clauses where simples et où vous faites autant que vous le pouvez avec des jointures au lieu de sous-sélections. Le but est de faire en sorte que "les enregistrements ne s'affichent pas" vous donne quelques endroits très spécifiques dans la requête à vérifier (est-il supprimé dans une jointure ou filtré dans une clause where?) Et donc l'équipe de maintenance peut réellement maintenir les choses.

En ce qui concerne l'évolutivité, gardez à l'esprit que plus la flexibilité du planificateur est grande, c'est aussi une bonne chose ...

Edit: Vous mentionnez qu'il s'agit de MySQL, il est donc peu probable que les vues fonctionnent aussi bien et les CTE sont hors de question. De plus, l'exemple donné n'est pas particulièrement long ou complexe, ce n'est donc pas un problème.

Chris Travers
la source
Remarque: J'ai eu des requêtes (pas dans MySQL, mais quand même ...) qui étaient suffisamment longues et complexes pour que les plans de requête générés ne soient pas optimaux. Dans ces cas, vous pouvez en effet obtenir des résultats plus rapides en divisant une requête extrêmement complexe en deux requêtes moins complexes. Cela dit, c'est rare, et j'écrirai généralement la requête complexe et découvrirai s'il y a un problème plutôt que de diviser la requête en petits morceaux de manière préventive.
RDFozz
8

En tant que personne qui doit prendre en charge / nettoyer ces requêtes volumineuses et compliquées, je dirais qu'il est préférable de les diviser en plusieurs petits morceaux faciles à comprendre. Ce n'est pas nécessairement mieux du point de vue des performances, mais vous donnez au moins à SQL une meilleure chance de proposer un bon plan de requête.

Rendez la vie plus facile aux gens qui vous suivent et ils vous diront de bonnes choses. Rendez-les durs et ils vous maudiront.

datagod
la source
2
l'inconvénient d'une chaîne de requêtes simples est cependant que l'état change considérablement entre elles, ce qui rend le débogage global de l'application plus complexe. C'est-à-dire que vous pouvez déboguer des requêtes SQL volumineuses souvent sous forme d'arbres, mais le code d'application obtient une instruction déboguée en vérifiant la façon dont l'état change dans les instructions. Les vrais problèmes ont à voir avec le fait que les sous-sélections et les vues en ligne sont également leurs propres arbres .....
Chris Travers
Dans mon cas, le seul qui doit gérer la base de données et le code est moi-même. Et surtout, ma question portait sur les performances de la requête.
Hamed Momeni
Vous devriez jeter un oeil à la façon dont j'écris mes gros processus par lots. Décomposez les choses en requêtes simples, très faciles à lire. Je suis biaisé parce que les requêtes que j'essaie de ranger sont généralement de plus de 1000 lignes.
datagod
5

Mes 2 cents sur les performances et l'évolutivité des requêtes de 2 mots clés:

Performances des requêtes : le parallélisme SQL Server fait déjà un très bon travail en décomposant les requêtes en recherches multi-threads, donc je ne sais pas dans quelle mesure vous améliorerez les performances des requêtes en le faisant pour SQL Server. Cependant, vous devrez regarder le plan d'exécution pour voir le degré de parallélisme que vous obtenez lorsque vous l'exécutez et comparer les résultats dans les deux sens. Si vous finissez par avoir à utiliser un indice de requête pour obtenir des performances identiques ou meilleures, alors l'OMI ne vaut pas la peine car l'indice de requête pourrait ne pas être optimal plus tard.

Évolutivité: la lecture des requêtes peut être plus facile, comme l'indique le code de données, et la diviser en requêtes distinctes est logique si vous pouvez également utiliser vos nouvelles requêtes dans d'autres domaines, mais si vous ne les utilisez pas également pour d'autres appels, alors ce sera encore plus de processus stockés à gérer pour 1 tâche, et IMO ne contribuerait pas à l'évolutivité.

Ali Razeghi
la source
2
RE: références "SQL Server" bien que l'OP n'ait pas spécifié de SGBDR particulier, je soupçonne qu'ils sont sur MySQL depuis les ticks arrière etLIMIT
Martin Smith
@MartinSmith Vous suspectez correctement. C'est MySQL.
Hamed Momeni
2

Parfois, il n'y a pas d'autre choix que de diviser la requête grande / complexe en petites requêtes. La meilleure façon de déterminer cela serait d'utiliser l' EXPLAINinstruction avec l' SELECTinstruction. Le nombre de traces / analyses que votre base de données va effectuer pour récupérer vos données est le produit des valeurs de "lignes" renvoyées par votre EXPLAINrequête. Dans notre cas, nous avons eu une requête joignant 10 tables. Pour un record particulier, la trace s'élevait à 409M qui bloguait notre base de données et poussait notre utilisation CPU de notre serveur DB plus de 300%. Nous avons pu récupérer les mêmes informations en fractionnant les requêtes beaucoup plus rapidement.

Donc, en bref, dans certains cas, le fractionnement d'une requête complexe / volumineuse est logique, mais dans d'autres, cela peut entraîner de nombreux problèmes de performances ou de maintenance et cela doit être traité au cas par cas.

user140665
la source