web-dev-qa-db-fra.com

Stocker une liste délimitée dans une colonne de base de données est-il vraiment si mauvais?

Imaginez un formulaire Web avec un ensemble de cases à cocher (toutes peuvent être sélectionnées). J'ai choisi de les enregistrer dans une liste de valeurs, séparées par des virgules, stockées dans une colonne du tableau de la base de données.

Maintenant, je sais que la bonne solution serait de créer une deuxième table et de normaliser correctement la base de données. L'implémentation de la solution simple a été plus rapide et je souhaitais avoir une preuve de concept de cette application rapidement et sans avoir à passer trop de temps dessus.

Je pensais que le gain de temps et la simplicité du code en valaient la peine dans ma situation. S'agit-il d'un choix de conception défendable ou dois-je l'avoir normalisé depuis le début?

Un peu plus de contexte, il s’agit d’une petite application interne qui remplace essentiellement un fichier Excel stocké dans un dossier partagé. Je demande aussi parce que je songe à nettoyer le programme et à le rendre plus facile à gérer. Il y a certaines choses là-dedans qui ne me satisfont pas entièrement, l'une d'entre elles étant le sujet de cette question.

342
Mad Scientist

En plus de violer First Normal Form en raison du groupe de valeurs récurrent stocké dans une seule colonne, les listes séparées par des virgules présentent de nombreux autres problèmes plus pratiques:

  • Impossible de s’assurer que chaque valeur est le bon type de données: aucun moyen d’empêcher 1,2,3, banane, 5
  • Impossible d'utiliser des contraintes de clé étrangère pour lier des valeurs à une table de recherche; aucun moyen d'appliquer l'intégrité référentielle.
  • Impossible d'appliquer l'unicité: aucun moyen d'empêcher 1,2,3,3,3,5
  • Impossible de supprimer une valeur de la liste sans extraire toute la liste.
  • Impossible de stocker une liste plus longue que celle contenue dans la colonne de chaîne.
  • Difficile de rechercher toutes les entités avec une valeur donnée dans la liste; vous devez utiliser une analyse de table inefficace. Peut avoir à recourir à des expressions régulières, par exemple dans MySQL:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • Difficile de compter les éléments de la liste ou de faire d'autres requêtes d'agrégats.
  • Difficile de joindre les valeurs à la table de référence à laquelle elles font référence.
  • Difficile de récupérer la liste dans un ordre trié.

Pour résoudre ces problèmes, vous devez écrire des tonnes de code d’application, en réinventant les fonctionnalités que le SGBDR fournit déjà beaucoup plus efficacement .

Les listes séparées par des virgules sont suffisamment fausses pour en faire le premier chapitre de mon livre: SQL Antipatterns: éviter les pièges de la programmation de base de données .

Il arrive que vous deviez employer la dénormalisation, mais @ (mentionne les poneys OMG) , ce sont des cas exceptionnels. Toute "optimisation" non relationnelle profite à un type de requête au détriment d'autres utilisations des données. Assurez-vous donc de savoir laquelle de vos requêtes doit être traitée de manière à mériter une dénormalisation.


* MySQL 8.0 ne prend plus en charge cette syntaxe d'expression Word-bound.

534
Bill Karwin

"Une des raisons était la paresse".

Cela sonne des cloches d'alarme. La seule raison pour laquelle vous devriez faire quelque chose comme ceci est que vous savez comment le faire "de la bonne façon", mais vous en êtes venu à la conclusion qu'il existe une raison tangible de ne pas le faire de cette façon.

Cela dit: si les données que vous choisissez de stocker de cette manière sont des données que vous n’aurez jamais besoin d’interroger, il peut alors s'avérer judicieux de les stocker de la manière que vous avez choisie.

(Certains utilisateurs contestaient la déclaration contenue dans mon paragraphe précédent, affirmant que "vous ne pouvez jamais savoir quelles exigences seront ajoutées à l'avenir". Ces utilisateurs sont soit mal avisés soit déclarent une conviction religieuse. Il est parfois avantageux de répondre aux avoir devant vous.)

40
Hammerite

Il y a de nombreuses questions sur SO demandant:

  • comment obtenir un nombre de valeurs spécifiques à partir de la liste séparée par des virgules
  • comment obtenir des enregistrements qui ont uniquement la même valeur spécifique 2/3/etc à partir de cette liste séparée par des virgules

Un autre problème avec la liste séparée par des virgules est de s'assurer que les valeurs sont cohérentes - stocker du texte signifie la possibilité de fautes de frappe ...

Ce sont tous des symptômes de données dénormalisées et expliquent pourquoi vous devez toujours modéliser des données normalisées. La dénormalisation peut être une optimisation de requête, à appliquer lorsque le besoin se présente réellement .

39
OMG Ponies

En général, tout est défendable s’il répond aux exigences de votre projet. Cela ne signifie pas que les gens seront d'accord ou voudront défendre votre décision ...

En général, stocker les données de cette manière est sous-optimal (par exemple, il est plus difficile de faire des requêtes efficaces) et peut entraîner des problèmes de maintenance si vous modifiez les éléments de votre formulaire. Peut-être auriez-vous pu trouver un terrain d'entente et utiliser à la place un entier représentant un ensemble d'indicateurs de bits?

18
bobbymcr

Oui, je dirais que c'est vraiment si grave. C'est un choix défendable, mais cela ne le rend pas correct ou bon.

Il casse la première forme normale.

Une autre critique est que l'insertion de résultats bruts directement dans une base de données, sans aucune validation ni liaison, vous laisse ouverte aux attaques par injection SQL.

Ce que vous appelez la paresse et le manque de connaissances en SQL sont de la matière des néophytes. Je recommanderais de prendre le temps de le faire correctement et de le voir comme une opportunité d'apprendre.

Ou laissez-le tel quel et apprenez la douloureuse leçon d'une attaque par injection SQL.

12
duffymo

J'avais besoin d'une colonne à valeurs multiples, elle pourrait être implémentée comme un champ xml

Il pourrait être converti en virgule si nécessaire

interroger une liste XML sur un serveur SQL à l'aide de Xquery .

En tant que champ XML, certaines préoccupations peuvent être résolues.

Avec CSV: Impossible de s'assurer que chaque valeur est le bon type de données: aucun moyen d'éviter 1,2,3, banane, 5

Avec XML: les valeurs d'une balise peuvent être forcées pour être du type correct


Avec CSV: Impossible d'utiliser des contraintes de clé étrangère pour lier des valeurs à une table de correspondance. aucun moyen d'appliquer l'intégrité référentielle.

Avec XML: toujours un problème


Avec CSV: Impossibilité d'imposer l'unicité: aucun moyen d'empêcher 1,2,3,3,3,5

Avec XML: toujours un problème


Avec CSV: Impossible de supprimer une valeur de la liste sans extraire la liste complète.

Avec XML: des éléments uniques peuvent être supprimés


Avec CSV: Difficile de rechercher toutes les entités ayant une valeur donnée dans la liste; vous devez utiliser une analyse de table inefficace.

Avec XML: le champ xml peut être indexé


Avec CSV: Difficile de compter les éléments de la liste ou de faire d'autres requêtes d'agrégat. **

Avec XML: pas particulièrement difficile


Avec CSV: Difficile de joindre les valeurs à la table de consultation qu'elles référencent. **

Avec XML: pas particulièrement difficile


Avec CSV: Difficulté pour extraire la liste dans un ordre trié.

Avec XML: pas particulièrement difficile


Avec CSV: Stocker des entiers en tant que chaînes prend environ deux fois plus d'espace que stocker des entiers binaires.

Avec XML: le stockage est encore pire qu'un csv


Avec CSV: Plus beaucoup de virgules.

Avec XML: les balises sont utilisées à la place des virgules


En bref, l'utilisation de XML élimine certains des problèmes liés à la liste délimitée ET peut être convertie en une liste délimitée à la demande

7
James A Mohler

Cela fait maintenant plus de 4 ans que j'utilise une liste de paires clé/valeur séparées par des tabulations dans une colonne NTEXT dans SQL Server et cela fonctionne. Vous perdez la possibilité de faire des requêtes, mais d'un autre côté, si vous avez une bibliothèque qui persiste/dérépie la paire clé-valeur, alors ce n'est pas une si mauvaise idée.

7
Raj

Oui, c'est est si mauvais. Mon point de vue est que si vous n'aimez pas utiliser les bases de données relationnelles, cherchez une alternative qui vous convienne mieux, il existe de nombreux projets "NOSQL" intéressants avec des fonctionnalités très avancées.

6
Robin

Je prendrais probablement le juste milieu: convertissez chaque champ du fichier CSV en une colonne distincte de la base de données, sans vous soucier de la normalisation (du moins pour le moment). À un moment donné, la normalisation peut-être devient intéressante, mais avec toutes les données placées dans une seule colonne, vous ne tirez pratiquement aucun avantage de l'utilisation d'une base de données. Vous devez séparer les données en champs/colonnes logiques/comme vous voulez les appeler avant de pouvoir les manipuler de manière significative.

0
Jerry Coffin

Si vous avez un nombre fixe de champs booléens, vous pouvez utiliser une INT(1) NOT NULL (ou BIT NOT NULL s'il existe) ou CHAR (0) (nullable) pour chacun. Vous pouvez également utiliser un SET (j'oublie la syntaxe exacte).

0
Solomon Ucko