Cours
Si vous ne souhaitez plus jamais être confronté à des données incohérentes et redondantes, la normalisation des bases de données est la solution idéale.
Vous connaissez certainement la frustration de mettre à jour les informations d'un client dans un tableau, pour ensuite découvrir que des versions obsolètes sont dispersées dans cinq autres tableaux. Vos requêtes renvoient des résultats contradictoires, vos rapports affichent des chiffres différents selon le tableau d'où ils proviennent et vous passez des heures à déboguer des problèmes d'intégrité des données qui ne devraient pas exister. Ces problèmes ne font que se multiplier à mesure que votre base de données s'agrandit.
La normalisation des bases de données élimine ces difficultés en organisant vos données selon des principes mathématiques éprouvés. Le processus utilise des formes normales pour s'assurer que chaque élément d'information n'existe qu'à un seul endroit, ce qui rend votre base de données fiable et efficace.
Je vais vous présenter le processus complet de normalisation, depuis les concepts de base jusqu'aux formes normales avancées, à l'aide d'exemples pratiques qui transforment des données désordonnées en structures de base de données claires et faciles à gérer.
Pourquoi la normalisation est-elle importante ?
La normalisation est ce qui empêche votre base de données de devenir un cauchemar en termes de maintenance. Examinons pourquoi une normalisation adéquate est importante pour les applications concrètes.
Redondance des données
La redondance est le facteur silencieux qui nuit à la performance des bases de données. Lorsque vous stockez les mêmes informations à plusieurs endroits, vous ne faites pas que gaspiller de l'espace de stockage, vous vous exposez également à des incohérences susceptibles de perturber la logique de votre application.
Sans normalisation, la mise à jour de l'adresse d'un client implique de rechercher tous les tableaux qui contiennent des données relatives à son adresse. Si vous en oubliez un, vos rapports présenteront des informations contradictoires. Vos utilisateurs voient des adresses différentes selon les écrans. Vos analyses deviennent peu fiables.
La normalisation résout ce problème en garantissant que chaque donnée se trouve à un seul endroit. Lorsque vous mettez à jour l'adresse de ce client, elle est automatiquement modifiée partout, car toutes les références renvoient à la même source.
Intégrité des données
L'intégrité devient à toute épreuve lorsque vous normalisez correctement. Les contraintes de clé étrangère empêchent la création d'enregistrements orphelins. Il n'est pas possible de supprimer accidentellement un client qui a encore des commandes en cours. Votre base de données applique les règles métier au niveau des données, et pas seulement dans le code de l'application.
Cela se traduit par moins de bugs, un code plus propre et des applications qui se comportent de manière prévisible, même lorsque plusieurs systèmes accèdent aux mêmes données.
Anomalies des données
Les anomalies de modification disparaissent avec une normalisation adéquate. Ces problèmes surviennent lorsque vous insérez, mettez à jour ou supprimez des données et créez des incohérences ou nécessitez des solutions de contournement complexes.
Les anomalies d'insertion vous obligent à ajouter des données factices uniquement pour créer un enregistrement. Les anomalies de mise à jour vous obligent à modifier les mêmes informations sur plusieurs lignes. La suppression des anomalies supprime plus d'informations que prévu lorsque vous supprimez un seul enregistrement.
Les bases de données normalisées éliminent ces problèmes en organisant les données de manière à ce que chaque fait n'apparaisse qu'une seule fois.
Performances et évolutivité
Les performances et l'évolutivité sont améliorées lorsque la structure de votre base de données est claire. Les tableaux normalisés sont généralement plus petits, ce qui se traduit par des requêtes plus rapides et une meilleure utilisation du cache. Les index fonctionnent plus efficacement sur des tableaux plus petits et plus ciblés.
Votre base de données peut évoluer horizontalement, car les données normalisées ont des limites claires. Vous pouvez partitionner les tableaux de manière logique sans dupliquer les informations entre les fragments.
Sécurité
La sécurité est plus facile à gérer dans les bases de données normalisées. Vous pouvez contrôler l'accès au niveau des tableaux en toute confiance, car les données sensibles sont stockées dans des emplacements spécifiques et bien définis. Vous n'avez pas à vous soucier des numéros de carte de crédit de vos clients qui pourraient être cachés dans des tableaux inattendus.
Les pistes d'audit sont également plus claires : vous savez exactement où les modifications ont été apportées et pouvez les suivre sans avoir à rechercher des données redondantes dispersées dans votre schéma.
En résumé, la normalisation transforme des données chaotiques en une base fiable qui évolue avec votre application.
Voyons maintenant quelles sont les conditions préalables à la normalisation.
Concepts clés et prérequis
Avant de commencer à normaliser des tableaux, il est important de comprendre comment fonctionne la normalisation. Examinons les concepts essentiels qui guideront vos décisions tout au long du processus.
Comprendre les clés dans la normalisation des bases de données
Les clés constituent la base de la conception des bases de données relationnelles : elles identifient les enregistrements et relient les tableaux entre eux.
Une clé primaire identifie de manière unique chaque ligne d'un tableau. Deux lignes ne peuvent pas avoir la même valeur de clé primaire, et celle-ci ne peut pas être nulle. Considérez-le comme un numéro de sécurité sociale pour vos données : chaque enregistrement en reçoit un unique, sans doublon.
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
email VARCHAR(255),
name VARCHAR(100)
);
Ici, « customer_id
» est la clé primaire. Chaque client reçoit un identifiant unique que vous utiliserez pour référencer ce client spécifique à partir d'autres tableaux.
Une clé candidate est une colonne (ou une combinaison de colonnes) pouvant servir de clé primaire. Votre tableau « customers
» peut contenir à la fois « customer_id
» et « email
» comme clés candidates, car ces deux clés identifient de manière unique les clients. Vous en sélectionnez une comme clé primaire, et les autres restent des clés candidates.
Les clés étrangères créent des relations entre les tableaux. Ils font référence à la clé primaire d'un autre tableau et établissent des connexions qui garantissent l'intégrité des données.
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
Le champ « customer_id
» dans le tableau « orders
» est une clé étrangère. Il doit correspondre à une entité de type « customer_id
» qui existe dans le tableau « customers
». Cela évite les commandes orphelines et garantit que chaque commande appartient à un client réel.
Les clés appliquent les règles métier au niveau de la base de données, ce qui rend vos données plus fiables qu'une validation effectuée uniquement au niveau de l'application.
Rôle des dépendances fonctionnelles
Les dépendances fonctionnelles décrivent comment les colonnes sont liées entre elles dans un tableau. Ils constituent la base mathématique qui guide les décisions de normalisation.
Une dépendance fonctionnelle existe lorsque la valeur d'une colonne détermine la valeur d'une autre colonne. Nous écrivons cela sous la forme « A → B
», ce qui signifie « A détermine B » ou « B dépend de A ».
Dans un tableau d'customers
, customer_id → email
car chaque ID client correspond à une seule adresse e-mail. Si vous connaissez l'identifiant du client, vous pouvez déterminer l'adresse e-mail avec certitude.
Image 1 - Exemple de dépendance fonctionnelle
Ici, customer_id → email
et customer_id → name
, car l'identifiant client détermine à la fois l'adresse e-mail et le nom.
Les dépendances fonctionnelles révèlent des problèmes de redondance.
Si vous avez un tableau où order_id → customer_name
, mais que vous stockez le nom du client dans chaque ligne de commande, vous avez une redondance. Le nom du client dépend de son identifiant, et non du numéro de commande.
La préservation des dépendances signifie que vos tableaux normalisés conservent toutes les dépendances fonctionnelles d'origine. Lorsque vous divisez un tableau lors de la normalisation, vous ne devez pas perdre la possibilité d'appliquer les règles métier qui existaient dans le tableau d'origine.
La garantie de décomposition sans perte ( ) vous permet de reconstruire le tableau d'origine en joignant les tableaux normalisés. Vous ne perdez aucune information lorsque vous divisez des tableaux : les jointures restituent exactement les mêmes données que celles avec lesquelles vous avez commencé.
Ces concepts fonctionnent ensemble : les dépendances fonctionnelles identifient ce qui doit être séparé, tandis que la préservation des dépendances et la décomposition sans perte garantissent que rien n'est endommagé au cours du processus.
La compréhension de ces relations vous aide à prendre des décisions de normalisation judicieuses qui améliorent votre base de données sans en altérer les fonctionnalités.
Processus de normalisation étape par étape
Passons maintenant en revue le processus de normalisation proprement dit, en commençant par des données désordonnées et en les transformant étape par étape. Chaque forme normale s'appuie sur la précédente, il n'est donc pas possible de passer directement de données non normalisées à la 3NF.
Première forme normale (1NF)
La première forme normale élimine les groupes répétitifs et garantit que chaque colonne contient des valeurs atomiques. Pour en savoir plus, veuillez consulter notre guide détaillé sur la première forme normale (1NF).
Les valeurs atomiques signifient que chaque cellule contient exactement une information : pas de listes, pas de valeurs séparées par des virgules, pas de points de données multiples entassés dans un seul champ. C'est la base qui rend tout le reste possible.
Voici ce qui enfreint la 1NF :
CREATE TABLE orders_bad (
order_id INT,
customer_name VARCHAR(100),
products VARCHAR(500),
quantities VARCHAR(50)
);
Image 2 - Tableau qui ne respecte pas la 1NF
Les colonnes « products
» et « quantities
» contiennent plusieurs valeurs séparées par des virgules. Il n'est pas facile de rechercher « toutes les commandes contenant des ordinateurs portables » ou de calculer les quantités totales sans analyse de chaîne.
Pour convertir cela en 1NF, séparez les groupes répétitifs en lignes distinctes :
-- First normal form (1NF)
CREATE TABLE orders_1nf (
order_id INT,
customer_name VARCHAR(100),
product VARCHAR(100),
quantity INT
);
Image 3 - Tableau conforme à la 1NF
À présent, chaque cellule contient exactement une valeur. Vous pouvez interroger, trier et agréger les données à l'aide d'opérations SQL standard.
Deuxième forme normale (2NF)
La deuxième forme normale supprime les dépendances partielles, c'est-à-dire lorsque des colonnes non clés dépendent uniquement d'une partie d'une clé primaire composite.
La deuxième forme normale (2NF) est plus complexe qu'il n'y paraît. Pour en savoir plus sur l', veuillez consulter notre guide détaillé.
Un tableau est en 2NF s'il est en 1NF et si chaque colonne non clé dépend de la clé primaire entière, et non d'une partie seulement.
Notre tableau 1NF présente un problème. Si nous utilisons order_id
et product
comme clé primaire composite, customer_name
dépend uniquement de order_id
, et non du produit. Cela crée une redondance : le nom du client est répété pour chaque produit d'une commande.
-- Still has partial dependencies
-- customer_name depends only on order_id, not on (order_id, product)
CREATE TABLE orders_1nf (
order_id INT,
customer_name VARCHAR(100), -- Partial dependency!
product VARCHAR(100),
quantity INT,
PRIMARY KEY (order_id, product)
);
Pour obtenir la 2NF, divisez le tableau en fonction des dépendances :
-- Orders table (customer info depends on order_id)
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_name VARCHAR(100)
);
-- Order items table (quantity depends on both order_id and product)
CREATE TABLE order_items (
order_id INT,
product VARCHAR(100),
quantity INT,
PRIMARY KEY (order_id, product),
FOREIGN KEY (order_id) REFERENCES orders(order_id)
);
Désormais, l'customer_name
n'apparaît qu'une seule fois par commande, ce qui élimine toute redondance. Chaque tableau comporte des colonnes qui dépendent de la clé primaire complète.
Troisième forme normale (3NF)
La troisième forme normale élimine les dépendances transitives, qui se produisent lorsque des colonnes non clés dépendent d'autres colonnes non clés au lieu de laclé primaire. Explorez la troisième forme normale (3NF) au-delà des bases.
Une dépendance transitive existe lorsque la « colonne A » détermine la « colonne B » et que la « colonne B » détermine la « colonne C », créant ainsi une dépendance indirecte de A à C.
Élargissons notre tableau des commandes avec les coordonnées des clients :
-- Has transitive dependencies
CREATE TABLE orders_2nf (
order_id INT PRIMARY KEY,
customer_name VARCHAR(100),
customer_city VARCHAR(50),
customer_state VARCHAR(50),
customer_zip VARCHAR(10)
);
Voici le problème : customer_name → customer_city
et customer_city → customer_state
. L'État dépend de la ville, pas directement de l'ordre. Cela crée une redondance : chaque commande provenant de la même ville répète les informations relatives à l'état.
Pour obtenir la 3NF, supprimez les dépendances transitives en créant des tableaux distincts :
-- Customers table (removes transitive dependencies)
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(100),
city_id INT,
FOREIGN KEY (city_id) REFERENCES cities(city_id)
);
-- Cities table
CREATE TABLE cities (
city_id INT PRIMARY KEY,
city_name VARCHAR(50),
state VARCHAR(50),
zip VARCHAR(10)
);
-- Orders table (now references customer, not customer details)
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
Désormais, toutes les informations géographiques sont regroupées en un seul endroit. Si une ville change d'État (ce qui est rare mais possible), vous mettez à jour une ligne au lieu de rechercher toutes les commandes provenant de cette ville.
Chaque forme normale résout des problèmes de redondance spécifiques tout en conservant la possibilité de reconstruire vos données d'origine à l'aide de jointures.
Formes normales avancées
Les trois premières formes normales permettent de traiter la plupart des problèmes rencontrés dans les bases de données réelles, mais certains cas particuliers nécessitent une normalisation plus poussée. Ces formes avancées traitent des problèmes de dépendance spécifiques que la 3NF ne peut pas résoudre.
Forme normale de Boyce-Codd (BCNF)
BCNF corrige un problème subtil que 3NF ne détecte pas : lorsqu'un tableau comporte des clés candidates qui se chevauchent.
La 3NF permet aux colonnes non clés de dépendre des clés candidates, mais la BCNF est plus stricte. Dans le BCNF, chaque déterminant (une colonne qui détermine une autre colonne) doit être une superclé, soit une clé primaire, soit une clé candidate.
Voici où la 3NF présente des lacunes :
-- Table in 3NF but violates BCNF
CREATE TABLE course_instructors (
student_id INT,
course VARCHAR(50),
instructor VARCHAR(50),
PRIMARY KEY (student_id, course)
);
Les règles métier sont les suivantes :
- Chaque étudiant peut suivre plusieurs cours.
- Chaque cours est dispensé par un seul formateur.
- Chaque formateur enseigne exactement un cours.
Cela crée des dépendances vers course → instructor
et instructor → course
. (student_id, course)
et (student_id, instructor)
sont tous deux des clés candidates, mais course
et instructor
se déterminent mutuellement sans être eux-mêmes des superclés.
Le problème apparaît lorsque vous essayez d'ajouter un nouvel instructeur sans étudiants. Vous ne pouvez pas insérer « Le professeur Smith enseigne la conception de bases de données » sans ajouter également un étudiant à ce cours.
Pour atteindre le BCNF, décomposez en fonction de la dépendance problématique :
-- BCNF solution
CREATE TABLE course_assignments (
course VARCHAR(50) PRIMARY KEY,
instructor VARCHAR(50) UNIQUE
);
CREATE TABLE student_enrollments (
student_id INT,
course VARCHAR(50),
PRIMARY KEY (student_id, course),
FOREIGN KEY (course) REFERENCES course_assignments(course)
);
Vous pouvez désormais ajouter des formateurs sans étudiants, et la structure de la base de données correspond exactement aux règles métier.
Quatrième forme normale (4NF)
La 4NF élimine les dépendances multivaluées, c'est-à-dire lorsqu'une colonne détermine plusieurs ensembles de valeurs indépendants.
Une dépendance à valeurs multiples existe lorsque la « colonne A » détermine plusieurs valeurs dans la « colonne B » et que ces valeurs sont indépendantes des autres colonnes du tableau.
Veuillez examiner ce tableau qui suit le cursus et les loisirs des étudiants :
-- Violates 4NF due to multi-valued dependencies
CREATE TABLE student_info (
student_id INT,
skill VARCHAR(50),
hobby VARCHAR(50),
PRIMARY KEY (student_id, skill, hobby)
);
Image 4 - Tableau qui ne respecte pas la 4NF
Le problème : student_id
détermine à la fois les compétences et les loisirs, mais les compétences et les loisirs sont indépendants les uns des autres. Lorsque l'élève 1 apprend une nouvelle compétence, vous devez créer des lignes pour chaque combinaison de loisirs. Lorsqu'ils se lancent dans un nouveau passe-temps, vous avez besoin de lignes pour chaque combinaison de compétences.
Cela crée une redondance considérable à mesure que le nombre de compétences et de loisirs augmente.
Pour atteindre la 4NF, séparez les dépendances multivaluées indépendantes :
-- 4NF solution
CREATE TABLE student_skills (
student_id INT,
skill VARCHAR(50),
PRIMARY KEY (student_id, skill)
);
CREATE TABLE student_hobbies (
student_id INT,
hobby VARCHAR(50),
PRIMARY KEY (student_id, hobby)
);
Vous pouvez désormais ajouter des compétences et des loisirs de manière indépendante sans créer d'explosions de produits cartésiens.
Cinquième et sixième formes normales (5NF et 6NF)
La forme normale 5NF (Project-Join Normal Form) élimine les dépendances de jointure : relations complexes qui nécessitent trois tableaux ou plus pour reconstruire les données sans perte.
Une dépendance de jointure existe lorsque vous ne pouvez pas reconstruire le tableau d'origine en joignant deux tableaux décomposés, mais que vous pouvez le reconstruire en joignant trois tableaux ou plus.
Veuillez prendre en considération les fournisseurs, les pièces et les projets en appliquant la règle suivante : « Un fournisseur ne peut fournir une pièce pour un projet que s'il fournit cette pièce ET travaille sur ce projet. »
-- Original table with join dependency
CREATE TABLE supplier_part_project (
supplier_id INT,
part_id INT,
project_id INT,
PRIMARY KEY (supplier_id, part_id, project_id)
);
Pour atteindre la 5NF, décomposez en trois relations binaires :
-- 5NF decomposition
CREATE TABLE supplier_parts (supplier_id INT, part_id INT);
CREATE TABLE supplier_projects (supplier_id INT, project_id INT);
CREATE TABLE project_parts (project_id INT, part_id INT);
Vous ne pouvez reconstruire des combinaisons fournisseur-pièce-projet valides qu'en joignant les trois tableaux, ce qui applique la règle métier au niveau du schéma.
6NF pousse la normalisation à l'extrême en plaçant chaque attribut dans son propre tableau avec des clés temporelles.
6NF est conçu pour les entrepôts de données et les bases de données temporelles où vous devez suivre l'évolution de chaque attribut au fil du temps de manière indépendante.
-- 6NF example for temporal data
CREATE TABLE customer_names (
customer_id INT,
name VARCHAR(100),
valid_from DATE,
valid_to DATE
);
CREATE TABLE customer_addresses (
customer_id INT,
address VARCHAR(200),
valid_from DATE,
valid_to DATE
);
Cela vous permet de suivre quand chaque attribut a été modifié sans affecter les autres, mais cela rend les requêtes complexes et est rarement utilisé en dehors des systèmes de bases de données temporelles spécialisées.
La plupart des applications s'arrêtent au niveau 3NF ou BCNF. Ces formes avancées résolvent des cas particuliers, mais ajoutent une complexité qui n'est pas justifiée pour les applications commerciales classiques.
Amélioration de SQL pour les débutants
Avantages et inconvénients de la normalisation
La normalisation n'est pas une solution miracle : elle résout des problèmes importants, mais crée de nouveaux défis, principalement liés à la complexité des requêtes SQL. Voici les avantages et les inconvénients de la normalisation de votre base de données.
Avantages de la normalisation
- L' de la redondance réduit signifie que votre base de données stocke chaque fait une seule fois, ce qui réduit les coûts de stockage et élimine les problèmes de synchronisation. Lorsque les données client sont regroupées dans un seul tableau au lieu d'être dispersées dans des dizaines d'autres, la mise à jour d'une adresse se résume à une simple opération sur une ligne. Il n'est plus nécessaire de rechercher dans des tableaux connexes, de s'inquiéter des mises à jour manquées ou des données incohérentes apparaissant dans les rapports.
- L' de la cohérence des données devient automatique lorsqu'il n'existe qu'une seule source fiable. Votre application ne peut pas afficher d'informations contradictoires, car celles-ci ne peuvent pas exister.
- Les mises à jour deviennent rapides et fiables car vous ne modifiez qu'une seule ligne au lieu de plusieurs dizaines. Veuillez saisir un nouveau client une seule fois, puis faites référence à celui-ci partout ailleurs à l'aide de clés étrangères. Supprimez une commande sans vous soucier des données orphelines dans les tableaux associés.
- Les contrôles de sécurité deviennent plus simples d' r lorsque les données sensibles sont clairement délimitées. Les informations relatives aux paiements des clients sont stockées dans un tableau spécifique avec des contrôles d'accès spécifiques. Vous n'avez pas à vous soucier des numéros de carte de crédit cachés dans des endroits inattendus.
- L'évolutivité améliore l', car les tableaux normalisés sont plus petits et plus ciblés. Les index fonctionnent mieux sur les tableaux de petite taille. Vous pouvez partitionner les données de manière logique sans dupliquer les informations entre les fragments.
- La collaboration au sein de l'équipe devient plus fluide et plus efficace lorsque chacun sait où se trouvent les données. Les nouveaux développeurs peuvent naviguer plus rapidement dans le schéma. Les administrateurs de bases de données peuvent optimiser les performances en toute confiance. Les analystes commerciaux peuvent rédiger des requêtes fiables sans remettre en question la qualité des données.
- Les stratégies de sauvegarde et de restauration bénéficient d'une plus claire, car les données associées ne sont pas réparties dans plusieurs tableaux déconnectés. Les contraintes de clé étrangère garantissent que vous ne pouvez pas restaurer des données partielles qui rompent l'intégrité référentielle.
Inconvénients et défis de la normalisation
- La complexité des requêtes augmente l', notamment lorsque des questions simples nécessitent plusieurs jointures pour obtenir une réponse. Souhaitez-vous consulter l'historique des commandes d'un client avec les noms des produits ? Dans un tableau dénormalisé, cela correspond à une seule requête. Dans une base de données normalisée, vous joignez les tableaux clients, commandes, articles de commande et produits. Plus il y a de jointures, plus il y a de risques d'erreurs et plus l'exécution des requêtes est lente.
- Les performances peuvent être affectées par l' lorsque vous joignez constamment des tableaux au lieu de lire à partir de tableaux uniques et larges. Chaque jointure ajoute une surcharge, en particulier lorsque votre base de données doit accéder à des données provenant de différents emplacements de stockage.
- Le temps de développement augmente l', car les développeurs doivent comprendre les relations entre les tables avant de rédiger les requêtes. Ce qui était auparavant une simple table «
SELECT
» devient une table «JOIN
» avec plusieurs tables et une gestion appropriée des clés étrangères. - La surnormalisation crée une complexité artificielle ( ) lorsque vous divisez des données qui appartiennent naturellement ensemble. Si vous normalisez le nom complet d'une personne en séparant le prénom, le deuxième prénom et le nom de famille dans des tableaux distincts, vous êtes probablement allé trop loin.
Voici un exemple concret : Un site de commerce électronique a normalisé les catégories de produits en six niveaux hiérarchiques. Des requêtes simples telles que « afficher tous les appareils électroniques » ont donné lieu à des jointures de sept tableaux qui ont pris plusieurs secondes au lieu de quelques millisecondes. La pureté théorique ne valait pas la peine d'être recherchée au détriment de la pratique.
- Les applications à forte intensité de lecture souffrent d' s lorsque la normalisation optimise les écritures, alors que la plupart des opérations sont des lectures. Les flux des réseaux sociaux, les tableaux de bord analytiques et les systèmes de reporting fonctionnent souvent mieux avec une certaine dénormalisation stratégique.
- Les frais généraux de maintenance augmentent de manière exponentielle avec le nombre de tableaux. Plus il y a de tableaux, plus il y a d'index à gérer, plus il y a de contraintes de clés étrangères à valider et plus les procédures de sauvegarde sont complexes.
La clé réside dans le fait de trouver le juste équilibre pour votre cas d'utilisation spécifique : normalisez suffisamment pour éviter les problèmes d'intégrité des données, mais pas au point de sacrifier les performances et la productivité des développeurs.
Performances et optimisation
La normalisation affecte différents types de systèmes de différentes manières : ce qui est bénéfique pour les systèmes transactionnels peut nuire aux systèmes analytiques. Voici comment optimiser les performances en fonction de vos modèles de charge de travail.
Considérations relatives aux systèmes OLTP et OLAP
Les systèmes OLTP bénéficient de la normalisation car ils traitent de nombreuses petites transactions ciblées qui modifient des enregistrements spécifiques.
Dans une application de commerce électronique, lorsqu'un client met à jour son adresse de livraison, vous modifiez une ligne dans le tableau des clients. Sans normalisation, vous devriez mettre à jour les informations d'adresse dans les tableaux clients, commandes, adresses de livraison et adresses de facturation, ce qui entraînerait de multiples écritures et augmenterait les conflits de verrouillage.
Les tableaux normalisés réduisent les conflits de verrouillage car les transactions affectent des ensembles de données plus petits et plus ciblés. Lorsque « l'utilisateur A » met à jour son profil pendant que « l'utilisateur B » passe une commande, il est probable qu'ils accèdent à des tableaux complètement différents. Cela se traduit par une meilleure concurrence et un traitement plus rapide des transactions.
Les opérations d'écriture deviennent atomiques et prévisibles dans les systèmes normalisés. Veuillez insérer une nouvelle commande en écrivant dans le tableau orders et le tableau order_items. Si l'une des opérations échoue, vous pouvez revenir en arrière sans problème, sans vous soucier des mises à jour partielles dispersées dans les structures dénormalisées.
Les systèmes OLAP présentent une situation différente : ils nécessitent une lecture rapide de grands ensembles de données et agr: ils nécessitent des lectures rapides dans de grands ensembles de données et agrègent souvent des données provenant de plusieurs tableaux connexes.
Prenons l'exemple d'une requête d'analyse des ventes : Veuillez afficher le chiffre d'affaires mensuel par catégorie de produits pour les deux dernières années. Un système normalisé nécessite la jointure des tableaux des commandes, des articles de commande, des produits et des catégories, ce qui peut représenter des millions de lignes avec des agrégations coûteuses.
Un tableau de données dénormalisé provenant d'un entrepôt de données et contenant des totaux mensuels précalculés répond à la même question à l'aide d'une simple requête d'GROUP BY
. Le compromis réside dans l'espace de stockage et la complexité des mises à jour, qui permettent en contrepartie des performances de requête beaucoup plus rapides.
Les approches hybrides sont particulièrement efficaces lorsque vous avez besoin à la fois d'intégrité transactionnelle et de performances analytiques. Maintenez la normalisation de votre système OLTP pour garantir l'intégrité des données, puis effectuez l'ETL vers des systèmes OLAP dénormalisés pour accélérer la génération de rapports.
Techniques visant à réduire la charge liée à la normalisation
- Une stratégie d' d'indexation adéquate transforme les performances des jointures dans les bases de données normalisées. Les colonnes de clé étrangère doivent toujours être indexées. Lorsque vous joignez des tableaux clients et commandes sur l'ID client, les deux tableaux doivent comporter des index sur cette colonne. Sans eux, la base de données effectue des analyses complètes des tableaux, ce qui nuit considérablement aux performances.
-- Must-have indexes for normalized tables
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);
- Les index composites facilitent les requêtes sur plusieurs colonnes courantes dans les schémas normalisés :
-- For queries filtering by customer and date range
CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);
- La mise en cache des résultats de requêtes ( ) élimine la surcharge liée aux jointures répétées pour les combinaisons de données fréquemment consultées. Redis ou Memcached peuvent stocker des résultats précalculés pour les requêtes multi-tableaux coûteuses.
- Le regroupement des connexions à la base de données réduit la charge liée à l'établissement de connexions pour les applications qui effectuent de nombreuses requêtes normalisées de petite taille.
- Vues matérialisées précontient les jointures complexes et stocke les résultats sous forme de tableaux physiques :
-- Pre-computed customer order summary
CREATE MATERIALIZED VIEW customer_order_summary AS
SELECT
c.customer_id,
c.name,
COUNT(o.order_id) as total_orders,
SUM(o.total_amount) as lifetime_value
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name;
- L' du partitionnement horizontal fonctionne bien avec les données normalisées, car les relations entre les tables fournissent des limites de partitionnement naturelles. Les fragments par client_id et les données de commande associées restent groupés.
- Les réplicas traitent les requêtes analytiques séparément des charges de travail transactionnelles. Acheminez les requêtes de reporting complexes vers des répliques en lecture seule tout en conservant les écritures dans la base de données principale.
- Les optimisations spécifiques à la base de données font une énorme différence :
- PostgreSQL : Veuillez utiliser «
EXPLAIN ANALYZE
» pour identifier les jointures lentes, puis régler « work_mem » pour les opérations de tri. - MySQL : Activez le cache de requêtes pour les instructions d'
SELECT
répétées, optimisez la taille du tampon d'JOIN
. - SQL Server : Utilisez les plans d'exécution des requêtes pour identifier les index manquants et activez la compression des pages pour les tableaux volumineux.
- PostgreSQL : Veuillez utiliser «
La clé réside dans la mesure avant l'optimisation. Analysez vos requêtes réelles afin d'identifier les goulots d'étranglement, puis appliquez des correctifs ciblés plutôt que de deviner ce qui pourrait être utile.
Dénormalisation : Compromis stratégiques
Il est parfois judicieux d'enfreindre les règles de normalisation, notamment lorsque la performance de lecture est plus importante qu'une organisation parfaite des données. Voici quand et comment dénormaliser sans créer de problèmes de maintenance.
- Les applications à forte intensité de lecture avec des jointures coûteuses sont les principales candidates à une dénormalisation stratégique.
- Les tableaux de bord en temps réel et les analyses de données nécessitent souvent des données dénormalisées pour atteindre les objectifs de performance. Lorsque les dirigeants souhaitent consulter des indicateurs de vente en temps réel, mis à jour toutes les quelques secondes, vous ne pouvez pas vous permettre d'effectuer des agrégations complexes à partir de tableaux normalisés.
- Les catalogues de produits en ligne dénormalisent souvent les informations relatives aux catégories. Au lieu de regrouper les produits → sous-catégories → catégories → catégories principales, de nombreux sites stockent le chemin d'accès complet à la catégorie directement avec chaque produit : « Électronique > Ordinateurs > Ordinateurs portables > Jeux vidéo. »
- Les techniques courantes de dénormalisation comprennent :
- Stockage des valeurs calculées: Conservez les totaux, les comptes ou les moyennes qui nécessiteraient autrement des requêtes d'agrégation.
- Aplatissement des hiérarchies: Stockez les chemins d'accès aux catégories de magasins, les structures organisationnelles ou les données imbriquées sous forme de champs plats.
- Duplication des données fréquemment utilisées: Copiez les noms des clients dans les enregistrements de commande et les titres des produits dans les articles du panier.
- Données relatives à la préadhésion: Enregistrer les informations du profil utilisateur avec les publications, les commentaires ou les enregistrements d'activité.
Tout réside dans la compréhension de vos habitudes d'accès. Si vous consultez les récapitulatifs des commandes de vos clients 100 fois plus souvent que vous ne mettez à jour leurs informations, il est judicieux de dupliquer le nom du client dans les enregistrements de commande.
Cependant, veuillez procéder à une dénormalisation sélective. Veuillez ne pas aplatir l'ensemble de votre schéma parce qu'un rapport s'exécute lentement. Veuillez corriger ce rapport tout en conservant la normalisation des autres.
Commencez par normaliser, puis dénormalisez en fonction des problèmes de performances réels. Une dénormalisation prématurée complique les mises à jour avant même que vous ne sachiez si vous avez réellement besoin d'une amélioration des performances.
Résumé de la normalisation des bases de données
En termes simples, la normalisation des bases de données élimine la redondance des données et garantit leur cohérence.
Cela implique toutefois des compromis en termes de complexité des requêtes et de performances. Il est essentiel de choisir le niveau qui correspond à votre charge de travail. Les systèmes OLTP bénéficient d'une normalisation complète grâce à la norme 3NF, tandis que les applications à forte intensité de lecture nécessitent souvent une dénormalisation stratégique pour gagner en vitesse.
Vous n'avez pas à choisir une seule approche. Maintenez la normalisation de votre base de données transactionnelle afin de garantir l'intégrité des données, puis utilisez des vues dénormalisées ou des bases de données analytiques distinctes pour la création de rapports. Cette stratégie hybride vous offre à la fois fiabilité et performances là où vous en avez le plus besoin.
Commencez par une normalisation adéquate, puis dénormalisez de manière sélective en fonction des problèmes de performances réels plutôt que de considérations théoriques.
Si vous souhaitezapprofondir vos compétences en matière de bases de données, ces cours constituent une excellente étape suivante :
Foire aux questions
Quels sont les principaux avantages de la normalisation dans la gestion des bases de données ?
La normalisation élimine la redondance des données, ce qui réduit les coûts de stockage et évite les incohérences dans votre base de données. Cela rend les mises à jour plus rapides et plus fiables, car vous n'avez qu'à modifier les informations à un seul endroit au lieu de les rechercher dans plusieurs tableaux. Les bases de données normalisées offrent également une meilleure intégrité des données grâce à des contraintes de clés étrangères, des contrôles de sécurité plus efficaces, car les données sensibles sont stockées dans des tableaux spécifiques, et une meilleure évolutivité, car les tableaux plus petits et plus ciblés fonctionnent mieux avec les index et le partitionnement.
Comment la normalisation améliore-t-elle l'intégrité des données ?
La normalisation garantit l'intégrité des données au niveau de la base de données grâce à des contraintes de clés étrangères et à l'élimination des données redondantes. Lorsque vous ne pouvez pas supprimer accidentellement un client qui a encore des commandes en cours ou insérer une commande sans client valide, votre base de données maintient automatiquement l'intégrité référentielle. Étant donné que chaque information n'existe qu'à un seul endroit, il n'y a pas de versions contradictoires des mêmes données dispersées dans plusieurs tableaux, ce qui évite les incohérences susceptibles de perturber la logique de l'application.
Quels sont les pièges courants de la normalisation ?
La surnormalisation crée une complexité inutile lorsque vous divisez des données qui vont naturellement ensemble, comme séparer le nom d'une personne en plusieurs tableaux. Cela entraîne un nombre excessif de jointures pour des requêtes simples et nuit aux performances. Un autre piège consiste à normaliser sans tenir compte de vos modèles d'accès réels. Si vous joignez constamment les mêmes tableaux pour des requêtes courantes, vous pourriez avoir besoin d'une dénormalisation stratégique. Une mauvaise indexation des colonnes de clés étrangères nuit également aux performances des bases de données normalisées, ralentissant considérablement les jointures.
Quel est l'impact de la dénormalisation sur les performances des bases de données ?
La dénormalisation améliore les performances de lecture en éliminant les jointures, ce qui peut considérablement accélérer les requêtes courantes, passant de 50 ms à 5 ms dans les applications à fort trafic. Cependant, cela rend les opérations d'écriture plus complexes, car les mises à jour doivent garantir la cohérence entre plusieurs copies dénormalisées des mêmes données. Cela augmente le risque d'incohérences dans les données et nécessite davantage de logique applicative pour assurer la synchronisation de l'ensemble. La dénormalisation nécessite également davantage d'espace de stockage, car vous dupliquez les données dans plusieurs tableaux.
Quelles sont les meilleures pratiques pour déterminer quand normaliser ou dénormaliser une base de données ?
Commencez par une normalisation adéquate vers la troisième forme normale (3NF) afin de garantir l'intégrité des données, puis dénormalisez de manière sélective en fonction des problèmes de performances réels plutôt que de considérations théoriques. Évaluez vos modèles de requêtes réels : si vous lisez les données 100 fois plus souvent que vous ne les mettez à jour, la dénormalisation peut s'avérer utile pour ces tableaux spécifiques. Utilisez des approches hybrides : normalisez votre base de données transactionnelle pour les écritures, puis créez des vues dénormalisées ou des bases de données analytiques distinctes pour la création de rapports. Veuillez toujours analyser les performances avant d'apporter des modifications, car un indexage approprié et l'optimisation des requêtes permettent souvent de résoudre les problèmes de normalisation apparents sans modifier le schéma.