Je trouve qu'il est très difficile d'écrire des requêtes SQL complexes impliquant des jointures sur de nombreuses tables (au moins 3-4) et impliquant plusieurs conditions imbriquées. Les requêtes que l'on me demande d'écrire sont facilement décrites par quelques phrases, mais peuvent nécessiter une quantité trompeuse de code pour se terminer. Je me retrouve souvent à utiliser des vues temporaires pour écrire ces requêtes, qui semblent un peu comme une béquille. Quels conseils pouvez-vous fournir pour faciliter ces requêtes complexes? Plus précisément, comment décomposer ces requêtes en étapes que je dois utiliser pour réellement écrire le code SQL?
Notez que je suis le SQL qu'on me demande d'écrire fait partie des devoirs pour un cours de base de données, donc je ne veux pas de logiciel qui fera le travail pour moi. Je veux vraiment comprendre le code J'écris.
Plus de détails techniques:
Je base la plupart de ces informations sur la simple tentative d'obtenir la "bonne" réponse, vous découvrirez donc peut-être des problèmes de performances. Inutile d'accélérer une requête incorrecte.
Comprendre les relations entre les tables - La plupart seront un à plusieurs. Connaissez le tableau "plusieurs". Identifiez les champs requis pour vos jointures.
Pensez aux scénarios de jointure GAUCHE - Sélectionnez tous les employés et leur salaire du mois dernier. Et s'ils n'avaient pas reçu de chèque de paie le mois dernier?
Connaître l'ensemble de résultats: 1) Dans une feuille de calcul, entrez manuellement au moins un enregistrement correct pour votre requête. 2) Écrivez la requête sous une forme suffisamment simple pour identifier le nombre d'enregistrements à renvoyer. Utilisez les deux pour tester votre requête afin de vous assurer que rejoindre une nouvelle table ne modifie pas le résultat.
Divisez votre requête en parties gérables - Vous n'avez pas besoin de tout écrire en même temps. Les requêtes complexes peuvent parfois être simplement une collection de requêtes simples.
Méfiez-vous des niveaux d'agrégation mixtes: Si vous devez mettre des valeurs mensuelles, trimestrielles et cumulatives dans le même ensemble de résultats, vous devrez les calculer séparément dans des requêtes regroupées sur différentes valeurs.
Savoir quand UNION Parfois, il est plus facile de diviser les sous-groupes en leurs propres instructions select. Si vous avez un tableau mélangé avec des gestionnaires et d'autres employés, et sur chaque colonne, vous devez effectuer des instructions Case en fonction de l'appartenance à l'un de ces groupes, il peut être plus facile d'écrire une requête et une union de gestionnaire dans une requête d'employé. Chacun contiendrait sa propre logique. Devoir inclure des éléments de différents tableaux dans différentes lignes est une utilisation évidente.
formules complexes/imbriquées - Essayez de mettre en retrait de manière cohérente et n'ayez pas peur d'utiliser plusieurs lignes. "CASE WHEN CASE WHEN CASE WHEN" vous rendra dingue. Prenez le temps d'y réfléchir. Enregistrez les calculs complexes pour la fin. Obtenez d'abord les enregistrements corrects sélectionnés. Ensuite, vous attaquez des formules complexes en sachant que vous travaillez avec les bonnes valeurs. Voir les valeurs utilisées dans les formules vous aidera à repérer les zones où vous devez prendre en compte les valeurs NULL et où gérer la division par erreur zéro.
Testez souvent lorsque vous ajoutez de nouvelles tables pour vous assurer d'obtenir toujours l'ensemble de résultats souhaité et de savoir quelle jointure ou clause est le coupable.
Indentation serait la première chose à faire, si vous ne le faites pas déjà. Non seulement c'est utile avec des requêtes même simples, mais c'est crucial quand il s'agit de jointures et de requêtes un peu plus complexes qu'un select top 1 [ColumnName] from [TableName]
.
Une fois mis en retrait correctement, rien n'interdit de ajouter des commentaires à l'intérieur de la requête elle-même, le cas échéant. N'en abusez pas: si le code est suffisamment explicite, l'ajout de commentaires nuira simplement à la clarté du code. Mais ils sont toujours les bienvenus pour les parties les moins explicites de la requête.
Notez que des requêtes plus longues (y compris des requêtes avec commentaires) signifieraient une plus grande utilisation de la bande passante entre votre serveur d'applications et votre serveur de base de données. Notez également qu'à moins que vous ne travailliez sur un produit à l'échelle de Google avec une énorme quantité de demandes par seconde, nécessitant une performance et une utilisation des ressources exceptionnelles, la taille ajoutée par les commentaires peut ne rien changer pour vous en termes de performances.
L'application du même style sur les tables, les colonnes, etc. aide également beaucoup la lisibilité. Lorsqu'une base de données héritée contient les tables PRODUCT
, users
, USERS_ObsoleteDONT_USE
, PR_SHIPMENTS
Et HRhbYd_UU
, Quelqu'un fait quelque chose de très mal.
Appliquer le même style sur les requêtes est également important. Par exemple, si vous écrivez des requêtes pour Microsoft SQL Server et que vous avez décidé d'utiliser [TableName]
Au lieu de TableName
, respectez-le. Si vous accédez à une nouvelle ligne après un select
, ne le faites pas dans seulement la moitié de vos requêtes, mais toutes.
N'utilisez pas *
, à moins qu'il n'y ait de bonnes raisons de le faire (comme dans if exists(select * from [TableName] where ...)
dans Microsoft SQL Server). Non seulement *
A un impact négatif sur les performances dans certaines bases de données (sinon la plupart), mais il n'est pas non plus utile pour le développeur qui utilise votre requête. De la même manière, un développeur doit accéder aux valeurs par nom, jamais par index.
Enfin, pour les sélections, il n'y a rien de mal à fournir un voir. Pour toute autre chose, procédures stockées peut également être utilisé en fonction du projet et des personnes¹ avec lesquelles vous travaillez².
¹ Certaines personnes détestent les procédures stockées. D'autres ne les aiment pas pour plusieurs raisons (parfaitement valables, du moins pour eux).
² Vos collègues, les autres élèves, votre professeur, etc.
Un peu dans le noir ici, mais si vous écrivez beaucoup de vues temporaires, vous ne vous êtes peut-être pas encore rendu compte que la plupart des endroits où vous pouviez mettre une table dans une instruction SQL, cette table peut être remplacée par une requête.
Ainsi, plutôt que de joindre la table A à la vue temporaire B, vous pouvez joindre la table A à la requête que vous avez utilisée comme vue temporaire B. Par exemple:
SELECT A.Col1, A.Col2, B.Col1,B.Col2
FROM (SELECT RealTableZ.Col1, RealTableY.Col2, RealTableY.ID as ID
FROM RealTableZ
LEFT OUTER JOIN RealTableY
ON RealTableZ.ForeignKeyY=RealTableY.ID
WHERE RealTableY.Col11>14
) As B
INNER JOIN A
ON A.ForeignKeyY=B.ID
Cet exemple est plutôt inutile, mais devrait expliquer la syntaxe.
Pour les vues qui ne sont pas "spéciales" (indexées, partitionnées), cela devrait entraîner le même plan de requête que si vous utilisiez une vue.
Pour faciliter l'écriture, vous pouvez vérifier chaque élément pour vous assurer d'obtenir ce que vous attendez avant de rédiger l'intégralité de la requête.
Mes excuses si c'est déjà un vieux chapeau pour vous.
Au lieu de vues temporaires, utilisez la clause WITH . Il est ainsi beaucoup plus facile de décomposer des requêtes volumineuses en parties plus petites et plus lisibles.
Comme toute autre chose, vous voulez décomposer le problème en parties gérables.
C'est vraiment IS comment vous résolvez des problèmes complexes, au fait.
Donc: vous voulez vérifier la sous-requête pour voir qu'elle retourne vraiment ce que vous voulez avant d'exécuter une requête externe dessus. Vous voulez essayer une jointure minimale de chaque table à laquelle vous vous joignez afin de voir que vous y réfléchissez bien. Des choses comme ça. Espérer tout taper et sortir exactement ce que vous voulez en un seul coup est tout simplement irréaliste.
Une instruction SQL, une fois qu'elle atteint un certain niveau de complexité, est fondamentalement un petit programme en soi. Cela fait une grande différence de vraiment comprendre comment les données sont combinées, sélectionnées, filtrées et sorties.