Lors de notre dernière réunion hebdomadaire, une personne qui n'a aucune expérience en administration de bases de données a soulevé cette question:
"Y aurait-il un scénario qui justifie le stockage de données en ligne (chaîne) au lieu de plusieurs lignes?"
Supposons une table appelée countryStates
où nous voulons stocker les états d'un pays; J'utiliserai les États-Unis pour cet exemple et ne répertorierai pas tous les États par souci de paresse.
Là, nous aurions deux colonnes; l'un appelé Country
et l'autre appelé States
. Comme discuté ici , et proposé par @ srutzky's réponse , le PK
sera le code défini par ISO 3166-1 alpha- .
Notre table ressemblerait à ceci:
+---------+-----------------------+-------------------------------------------------------+
| Country | States | StateName |
+---------+-----------------------+-------------------------------------------------------+
| USA | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+
En posant cette même question à un ami développeur, il a dit que du point de vue de la taille du trafic de données, cela pourrait être utile, mais pas si nous devons manipuler ces données. Dans ce cas, il devrait y avoir une intelligence sur le code d'application qui pourrait transformer cette chaîne en liste (disons que le logiciel qui a accès à cette table doit créer une combo).
Nous avons conclu que ce modèle n'est pas très utile, mais je me méfie qu'il pourrait y avoir un moyen de le rendre utile.
Ce que je voudrais demander, c'est si l'un d'entre vous a déjà vu, entendu ou fait quelque chose comme ça d'une manière qui fonctionne.
Pour commencer, le titre actuel de la question faisant référence au "stockage des données sous forme de chaîne au lieu de colonnes" est un peu déroutant. Lorsque vous parlez de stocker des données sous forme de chaînes au lieu de quelque chose d'autre, cela fait généralement référence à la sérialisation de tout dans un format de chaîne au lieu d'un type de données correct/fort (par exemple INT
ou DATETIME
). Mais si vous vous interrogez sur le stockage de données sous forme de valeurs multiples dans un seul champ par opposition à des lignes distinctes, c'est un peu différent. Et pour être honnête, bien que la concaténation de valeurs se fasse le plus facilement avec des chaînes, elle peut également l'être avec les types INT
et BINARY
, soit en masquant les bits, soit en réservant de manière similaire certaines positions à avoir significations différentes. Étant donné que la deuxième interprétation est ce qui est réellement demandé, sur la base du texte de la Question, abordons cela.
En un mot: Non. Si vous stockez des points de données réels, cela ne fera que causer de la douleur (en termes de code et de performances) car c'est une complication inutile. S'il s'agit d'une valeur qui ne sera jamais stockée que comme une seule unité, mise à jour comme une seule unité et jamais désassemblée dans la base de données, cela pourrait être correct car elle est à peu près analogue au stockage d'une image ou d'un PDF. Sinon, toute tentative d'analyse des données invalidera l'utilisation de tout index (par exemple, en utilisant LIKE '%something%'
, ou CHARINDEX
, ou PATINDEX
, ou SUBSTRING
, etc.).
Si vous devez stocker des valeurs distinctes dans un seul champ d'une seule ligne, il existe des moyens plus appropriés de le faire: XML ou JSON. Ce sont des formats analysables ( XML / JSON ) et XML peut même être - indexé . Mais idéalement, ces données seraient stockées dans des champs correctement saisis afin de pouvoir être vraiment utiles.
Et n'oubliez pas que le but d'un SGBDR est de stocker des données afin qu'elles puissent être récupérées et manipulées le plus efficacement possible, dans les limites imposées en étant ACIDE - conforme. La récupération des valeurs concaténées est suffisamment mauvaise en raison de la nécessité d'analyser les valeurs en premier, et cela n'est pas indexable. Mais manipuler signifie souvent remplacer l'intégralité du blob juste pour en mettre à jour une partie (en supposant qu'aucun modèle n'existe à utiliser avec une fonction REPLACE
). Le type de données XML permet au moins XML DML pour les mises à jour simplistes, bien que celles-ci ne soient toujours pas aussi rapides qu'une simple mise à jour de données correctement modélisées.
En outre, étant donné un scénario tel que ce qui est montré dans la question ci-dessus, en concaténant tous les StateCodes ensemble, vous ne pourriez pas clé étrangère (dans les deux sens) ces valeurs.
Et si les besoins de l'entreprise changent au fil du temps et que vous avez besoin de suivre les propriétés supplémentaires de ces articles? En termes d '"États", qu'en est-il des capitales, ou de la population, ou d'un ordre de tri, ou autre chose? Stocké correctement en tant que lignes, vous pouvez ajouter plus de colonnes pour des propriétés supplémentaires. Bien sûr, vous pouvez avoir plusieurs niveaux de données analysables, tels que |StateCode,Capital,Population |StateCode,Capital,Populate|...
mais j'espère que tout le monde peut voir le problème se développer de façon exponentielle hors de contrôle. Bien sûr, ce problème particulier est assez facilement traité avec les formats XML et JSON, et c'est leur valeur, comme mentionné ci-dessus. Mais vous auriez toujours besoin d'une très bonne raison d'utiliser l'un ou l'autre comme moyen initial de modélisation, car ni l'un ni l'autre ne sera aussi efficace que l'utilisation de champs discrets dans des lignes séparées.
J'ai effectivement utilisé quelque chose comme ça dans un but très limité. Nous avons créé un tableau d'en-têtes pour les fichiers de sortie. Ils ont été spécifiquement construits et n'étaient pour la plupart que des en-têtes de colonnes, mais pas tout à fait. Donc, les données ressemblaient à quelque chose
OutputType OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo Name|JobName|JobTitle
Il ressemblait essentiellement à une liste délimitée. Et d'une certaine façon, ça l'était. Mais pour nous, c'était une seule longue chaîne.
Voilà l'astuce ici. Si vous jamais prévoyez d'analyser la liste, cela vaut la peine d'enregistrer la liste. Si toutefois vous avez besoin ou même besoin d'analyser la liste, cela vaut la peine d'avoir plus d'espace et de temps pour la diviser et l'enregistrer dans des lignes distinctes.
Je l'ai utilisé une fois avec une table plutôt petite, par exemple:
CREATE TABLE t1 (
ID number,
some_feature varchar2(100),
valid_channels varchar2(100));
CREATE TABLE channel_def (
channel varchar2(100));
Et puis stocker les valeurs CRM,SMS,SELF-CARE
en valid_channel
.
La table entière a quelque chose comme 10 enregistrements. valid_channel
contient des valeurs qui devraient en fait être dans une table de liaison qui décrit la relation plusieurs-à-plusieurs. Table t1
ne va pas être utilisé de manière intensive, nous avons donc décidé de suivre cette voie. Certaines politiques ont cependant été impliquées dans cette décision (voir ci-dessous).
Mais en général je l'évite, ce n'est pas 3NF.
L'endroit où je travaille actuellement a des dizaines de telles colonnes partout. Leur justification est que cela facilite leurs requêtes: au lieu de joindre trois tables à l'aide de la table de liaison, elles peuvent aller directement à la table de définition à l'aide de LIKE
. Par exemple.
SELECT *
FROM t1
INNER JOIN channel_def cd
ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';
Horrible + sur Oracle, il désactive l'utilisation de l'index en raison du démarrage de '%,'
.
Cela a été fait ici sur SE. Comme Marc Gravell écrit :
... Après réflexion et réflexion, nous nous sommes installés sur une représentation naturelle délimitée par des tubes (barres), avec des tubes de début/fin, de sorte que ".net c #" devient simplement "| .net | c # |". Cela a des vertus:
- très simple à analyser
- la mise à jour en masse et la suppression des balises peuvent être effectuées avec un simple remplacement (y compris les tuyaux, pour éviter de remplacer les correspondances de mi-balise)
- ...
Ce "nouveau format" était la prochaine étape de "l'ancien format" qui était un peu différent et a été choisi pour utiliser la fonction de recherche de texte intégral de SQL Server, donc certains des avantages ne sont pas pertinents si vous le faites à partir de zéro.
Ils n'ont vraisemblablement pas complètement normalisé la chose à la fois pour la quantité de travail et pour des raisons de performance.
Eh bien, l'un des principaux avantages possibles de l'utilisation de chaînes et d'autres types de données est de les envoyer de SQL Server à C #, C, C++ (etc.) à l'aide de SQLCLR lorsque des performances absolues peuvent être nécessaires. Vous pouvez même créer une vue ou une procédure stockée pour représenter des données relationnelles de manière non relationnelle - comme vous l'avez fait avec votre exemple ci-dessus à cet effet.
Voir cet exemple:
http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/
par Wikipedia: SQL CLR ou SQLCLR (SQL Common Language Runtime) est une technologie pour l'hébergement du moteur d'exécution de langage commun Microsoft .NET dans SQL Server. Le SQLCLR permet à du code managé d'être hébergé et exécuté dans l'environnement Microsoft SQL Server.
À mon avis, la réponse serait non. Je n'ai pas utilisé cette approche et je l'éviterais - je ne peux pas penser à une raison pour laquelle je choisirais cette voie. Vous vous penchez vers le monde de JSON/NoSQL avec un tableau.
Nous avions des choix de conception similaires dans un rôle précédent où l'équipe d'architectes voulait avoir un champ "Données" qui était délimité puis converti en binaire. Nous n'avons finalement pas choisi cette voie pour plusieurs raisons.
Si vous deviez vous joindre à ce type de données, ce serait une expérience laide. La mise à jour d'éléments uniques de la chaîne serait également désagréable.