web-dev-qa-db-fra.com

Stockage de JSON dans une base de données ou création d'une nouvelle colonne pour chaque clé

J'implémente le modèle suivant pour le stockage des données relatives à l'utilisateur dans ma table - j'ai 2 colonnes - uid (clé primaire) et une colonne meta qui stocke d'autres données sur l'utilisateur au format JSON. 

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------

Est-ce une meilleure façon (en termes de performances, de conception) que le modèle à une colonne par propriété, où la table aura plusieurs colonnes telles que uid, name, emailid

Ce que j’aime dans le premier modèle, c’est que vous pouvez ajouter autant de champs que possible sans limitation. 

De plus, je me demandais maintenant que j'ai implémenté le premier modèle. Comment puis-je effectuer une requête dessus, comme, je veux récupérer tous les utilisateurs qui ont un nom comme "foo"?

Question - Quel est le meilleur moyen de stocker des données relatives à l'utilisateur (en gardant à l'esprit que le nombre de champs n'est pas fixe) dans une base de données utilisant - JSON ou colonne par champ? De plus, si le premier modèle est implémenté, comment interroger la base de données comme décrit ci-dessus? Devrais-je utiliser les deux modèles en stockant toutes les données pouvant être recherchées par une requête dans une ligne séparée et les autres données en JSON (est une ligne différente)? 


Mettre à jour

Puisqu'il n'y aura pas trop de colonnes sur lesquelles j'ai besoin d'effectuer une recherche, est-il judicieux d'utiliser les deux modèles? Clé par colonne pour les données que je dois rechercher et JSON pour les autres (dans la même base de données MySQL)?

146
ShuklaSannidhya

Mis à jour le 4 juin 2017

Étant donné que cette question/réponse a acquis une certaine popularité, j'ai pensé que cela valait la peine d'être mis à jour.

Lors de la publication de cette question, MySQL ne prenait pas en charge les types de données JSON et la prise en charge de PostgreSQL n'en était qu'à ses balbutiements. Depuis la version 5.7, MySQL prend désormais en charge un type de données JSON (dans un format de stockage binaire), et PostgreSQL _ JSONB a considérablement mûri. Les deux produits fournissent des types JSON performants pouvant stocker des documents arbitraires, notamment la prise en charge de l'indexation de clés spécifiques de l'objet JSON.

Cependant, je maintiens toujours ma déclaration initiale selon laquelle votre préférence par défaut, lors de l'utilisation d'une base de données relationnelle, devrait toujours être colonne par valeur. Les bases de données relationnelles reposent toujours sur l'hypothèse que les données qu'elles contiennent seront assez bien normalisées. Le planificateur de requêtes dispose de meilleures informations d'optimisation lorsque vous consultez des colonnes plutôt que des clés dans un document JSON. Des clés étrangères peuvent être créées entre les colonnes (mais pas entre les clés dans les documents JSON). Important: si la majorité de votre schéma est suffisamment volatile pour justifier l’utilisation de JSON, vous voudrez peut-être au moins déterminer si une base de données relationnelle est le bon choix.

Cela dit, peu d'applications sont parfaitement relationnelles ou orientées document. La plupart des applications ont un mélange des deux. Voici quelques exemples où j'ai personnellement trouvé JSON utile dans une base de données relationnelle:

  • Lorsque vous stockez des adresses électroniques et des numéros de téléphone pour un contact, leur stockage en tant que valeurs dans un tableau JSON est beaucoup plus facile à gérer que plusieurs tables séparées.

  • Enregistrement des préférences utilisateur de clé/valeur arbitraires (où la valeur peut être booléenne, textuelle ou numérique et pour laquelle vous ne souhaitez pas avoir des colonnes séparées pour différents types de données)

  • Stockage des données de configuration sans schéma défini (si vous construisez Zapier ou IFTTT et devez stocker des données de configuration pour chaque intégration)

Je suis sûr qu'il y en a d'autres aussi, mais ce ne sont que quelques exemples rapides.

Réponse originale

Si vous voulez vraiment pouvoir ajouter autant de champs que vous voulez sans aucune limitation (autre qu'une limite de taille de document arbitraire), envisagez une solution NoSQL telle que MongoDB.

Pour les bases de données relationnelles: utilisez une colonne par valeur. Le fait de placer un blob JSON dans une colonne rend la requête pratiquement impossible (et extrêmement lente lorsque vous trouvez une requête qui fonctionne).

Les bases de données relationnelles tirent parti des types de données lors de l'indexation et doivent être implémentées avec une structure normalisée.

Remarque: cela ne veut pas dire que vous ne devriez jamais stocker JSON dans une base de données relationnelle. Si vous ajoutez de vraies métadonnées ou si votre JSON décrit des informations qui n'ont pas besoin d'être interrogées et sont uniquement utilisées pour l'affichage, il peut être excessif de créer une colonne distincte pour tous les points de données.

153
Colin M

Comme la plupart des choses "ça dépend". Stocker des données dans des colonnes ou des fichiers JSON n’est pas correct ou mauvais/bon ou mauvais. Cela dépend de ce que vous devez faire plus tard. Quelle est votre manière prévue d'accéder à ces données? Aurez-vous besoin de faire référence à d'autres données? 

D’autres personnes ont à peu près répondu à la question du compromis technique.

Peu de gens ont expliqué que votre application et ses fonctionnalités évoluaient avec le temps et l'impact de cette décision de stockage des données sur votre équipe. 

Parce que l'une des tentations d'utiliser JSON est d'éviter la migration du schéma. Par conséquent, si l'équipe n'est pas disciplinée, il est très facile de coller une autre paire clé/valeur dans un champ JSON. Il n'y a pas de migration pour cela, personne ne se souvient à quoi ça sert. Il n'y a pas de validation dessus. 

Mon équipe a utilisé JSON à côté de colonnes traditionnelles dans Postgres et au début, c’était la meilleure chose depuis le pain en tranches. JSON était attrayant et puissant. Jusqu'au jour où nous avons réalisé que la flexibilité avait un coût et qu'elle devenait soudain un réel problème. Parfois, ce point surgit très rapidement et il devient difficile de changer, car nous avons construit beaucoup d'autres choses en plus de cette décision de conception.

Les heures supplémentaires, l'ajout de nouvelles fonctionnalités et le fait que les données soient au format JSON ont conduit à des requêtes plus complexes que celles qui auraient été ajoutées si nous nous en tenions aux colonnes traditionnelles. Nous avons ensuite commencé à regrouper certaines valeurs de clé dans des colonnes afin de pouvoir créer des jointures et comparer des valeurs. Mauvaise idée. Maintenant, nous avons eu une duplication. Un nouveau développeur viendrait à bord et serait confus? Quelle est la valeur dans laquelle je devrais économiser? Le JSON ou la colonne?

Les champs JSON devenaient des coffres à ordures pour de petits morceaux de ceci et de cela. Aucune validation des données au niveau de la base de données, aucune cohérence ou intégrité entre les documents. Cela a amené toute cette responsabilité dans l'application au lieu d'obtenir une vérification de type et de contrainte difficile à partir de colonnes traditionnelles.

En regardant en arrière, JSON nous a permis d’itérer très rapidement et d’obtenir quelque chose par la porte. C'était super. Cependant, après avoir atteint une certaine taille, la flexibilité de notre équipe nous a également permis de nous accrocher à une longue dette de dette technique qui a ensuite ralenti l'évolution de l'évolution des fonctionnalités. Utiliser avec précaution.

Réfléchissez longuement à la nature de vos données. C'est la base de votre application. Comment les données seront-elles utilisées au fil du temps? Et comment est-il susceptible de changer?

44
Homan

Il suffit de le jeter, mais WordPress a une structure pour ce genre de choses (au moins, WordPress a été le premier endroit où je l'ai observé, il est probablement d'origine ailleurs).

Il permet des clés illimitées et est plus rapide à rechercher qu’à utiliser un blob JSON, mais pas aussi rapidement que certaines solutions NoSQL.

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

MODIFIER

Pour stocker l'historique/plusieurs clés

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

et interrogez via quelque chose comme ceci:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
27
Adam

l'inconvénient de l'approche est exactement ce que vous avez mentionné:

il est TRÈS lent à trouver des choses, puisque vous devez chaque fois effectuer une recherche de texte dessus. 

valeur par colonne correspond à la place de la chaîne entière.

Votre approche (données JSON) convient parfaitement aux données pour lesquelles vous n'avez pas besoin de chercher et que vous devez simplement afficher avec vos données normales.

Edit: Juste pour clarifier, ce qui précède s’applique aux bases de données relationnelles classiques. NoSQL utilise JSON en interne et constitue probablement une meilleure option si tel est le comportement souhaité.

13
Nick Andriopoulos

Fondamentalement, le premier modèle que vous utilisez s'appelle le stockage basé sur des documents. Vous devriez jeter un coup d'œil à la base de données populaire NoSQL telle que MongoDB et CouchDB . En gros, dans les bases de documents basées sur des documents, vous stockez des données dans des fichiers JSON, puis vous pouvez interroger ces fichiers JSON.

Le deuxième modèle est la structure de base de données relationnelle populaire. 

Si vous voulez utiliser une base de données relationnelle comme MySql, je vous conseillerais de n'utiliser que le deuxième modèle. Il est inutile d'utiliser MySql et de stocker des données comme dans le premier modèle .

Pour répondre à votre deuxième question, il n’existe aucun moyen d’interroger un nom comme «foo» si vous utilisez le premier modèle .

8
Girish

Il semble que vous hésitiez principalement à utiliser un modèle relationnel ou non.

Dans l'état actuel des choses, votre exemple correspondrait assez bien à un modèle relationnel, mais le problème peut bien sûr se poser lorsque vous devez faire évoluer ce modèle.

Si vous ne disposez que d'un ou de plusieurs niveaux d'attributs prédéterminés pour votre entité principale (utilisateur), vous pouvez toujours utiliser un modèle EAV (Entity Attribute Value) dans une base de données relationnelle. (Cela a aussi ses avantages et ses inconvénients.)

Si vous prévoyez d'obtenir des valeurs moins structurées que vous voudrez rechercher à l'aide de votre application, MySQL n'est peut-être pas le meilleur choix ici.

Si vous utilisiez PostgreSQL, vous pourriez potentiellement tirer le meilleur parti des deux mondes. (Ceci vraiment dépend de la structure réelle des données ici ... MySQL n'est pas nécessairement le mauvais choix non plus, et les options NoSQL peuvent être intéressantes, je ne fais que suggérer des alternatives.)

En effet, PostgreSQL peut construire des index sur des fonctions (immuables) (ce que MySQL ne peut pas, autant que je sache) et dans les versions récentes, vous pourriez utiliser directement PLV8 sur les données JSON pour construire des index sur des éléments d'intérêt JSON spécifiques , ce qui améliorerait la vitesse de vos requêtes lors de la recherche de ces données.

MODIFIER:

Puisqu'il n'y aura pas trop de colonnes sur lesquelles j'ai besoin d'exécuter recherche, est-il sage d'utiliser les deux modèles? Clé par colonne pour les données J'ai besoin de rechercher et JSON pour les autres (dans la même base de données MySQL)?

Mélanger les deux modèles n'est pas nécessairement une mauvaise chose (en supposant que l'espace supplémentaire soit négligeable), mais cela peut poser problème si vous ne vous assurez pas que les deux ensembles de données sont synchronisés: votre application ne doit jamais changer l'un sans mettre à jour l'autre. .

Un bon moyen d'y parvenir serait de faire effectuer la mise à jour automatique par un déclencheur, en exécutant une procédure stockée dans le serveur de base de données chaque fois qu'une mise à jour ou une insertion est effectuée. Autant que je sache, le langage de procédure stockée MySQL manque probablement de prise en charge pour tout type de traitement JSON. Encore une fois, PostgreSQL avec le support PLV8 (et éventuellement un autre SGBDR avec des langages de procédures stockés plus flexibles) devrait être plus utile (la mise à jour automatique de votre colonne relationnelle à l'aide d'un déclencheur est assez similaire à la mise à jour d'un index).

4
Bruno

un peu de temps passé sur la table sera une surcharge. disons pour OLAP. si j'ai deux tables on est la table ORDERS et l'autre est ORDER_DETAILS. Pour obtenir tous les détails de la commande, nous devons joindre deux tables, ce qui ralentira la requête lorsque aucun nombre de lignes dans les tables ne sera augmenté. si nous ajoutons une chaîne/un objet JSON dans l'entrée ORDERS respective, JOIN sera évité. ajouter la génération de rapports sera plus rapide ...

1
Ravindra

réponse courtevous devez vous mélanger entre eux, 

1
Ahmedfraije Aa

Vous essayez d’intégrer un modèle non relationnel dans une base de données relationnelle. Je pense que vous seriez mieux servi en utilisant une base de données NoSQL telle que MongoDB . Il n'y a pas de schéma prédéfini qui corresponde à votre exigence de ne pas limiter le nombre de champs (voir l'exemple typique de la collection MongoDB). Consultez la documentation MongoDB pour avoir une idée de la manière dont vous interrogeriez vos documents, par exemple.

db.mycollection.find(
    {
      name: 'sann'
    }
)
1
Chris L

Comme d'autres l'ont souligné, les requêtes seront plus lentes. Je suggérerais d'ajouter au moins une colonne '_ID' pour interroger par cela à la place.

0
Pants