Comment stocker des tableaux dans MySQL?

119

J'ai deux tables dans MySQL. Table Person contient les colonnes suivantes:

id | name | fruits

La fruitscolonne peut contenir null ou un tableau de chaînes comme ('pomme', 'orange', 'banane'), ou ('fraise'), etc. La deuxième table est Table Fruit et comporte les trois colonnes suivantes:

____________________________
fruit_name | color  | price
____________________________
apple      | red    | 2
____________________________
orange     | orange | 3
____________________________
...,...

Alors, comment dois-je concevoir la fruitscolonne dans la première table afin qu'elle puisse contenir un tableau de chaînes qui prennent des valeurs de la fruit_namecolonne dans la deuxième table? Puisqu'il n'y a pas de type de données de tableau dans MySQL, comment dois-je le faire?

Tonga
la source
1
que diriez-vous de l'ajouter en tant qu'entrées séparées, orange, 2, 1, rose, 2, 1, etc. et ensuite vous pouvez utiliser des requêtes pour les traiter comme s'il s'agissait de tableaux.
Sai
@JanusTroelsen: Je n'utilise pas PHP pour lire / écrire des bases de données. Alors, y a-t-il une manière universelle de le faire?
tonga
1
@tonga vérifie mon violon est-ce ce que tu veux?
echo_Me

Réponses:

164

La bonne façon de procéder consiste à utiliser plusieurs tables et JOINcelles - ci dans vos requêtes.

Par exemple:

CREATE TABLE person (
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(50)
);

CREATE TABLE fruits (
`fruit_name` VARCHAR(20) NOT NULL PRIMARY KEY,
`color` VARCHAR(20),
`price` INT
);

CREATE TABLE person_fruit (
`person_id` INT NOT NULL,
`fruit_name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`person_id`, `fruit_name`)
);

Le person_fruittableau contient une ligne pour chaque fruit auquel une personne est associée et relie efficacement les tableaux personet fruitsensemble, IE

1 | "banana"
1 | "apple"
1 | "orange"
2 | "straberry"
2 | "banana"
2 | "apple"

Lorsque vous souhaitez récupérer une personne et tous ses fruits, vous pouvez faire quelque chose comme ceci:

SELECT p.*, f.*
FROM person p
INNER JOIN person_fruit pf
ON pf.person_id = p.id
INNER JOIN fruits f
ON f.fruit_name = pf.fruit_name
Méchant loup
la source
4
La troisième table est la table des liens entre Person et Fruit. Donc, si une personne a 100 fruits. J'ai besoin de créer 100 lignes dans le troisième tableau, non? Est-ce efficace?
tonga
1
@tonga Exactement, chacune des 100 lignes aurait le même person_idmais un différent fruit_name. Il s'agit en fait d'une mise en œuvre de la théorie de la réponse de Janus.
Bad Wolf
1
Est-il toujours vrai que toute relation entre deux tables doit être stockée dans la troisième table? Puis-je simplement faire une requête pour trouver la relation en stockant simplement les clés primaires de deux tables?
tonga
2
Oui, c'est ainsi que l'exemple est configuré maintenant. Toute information sur la personne doit figurer dans le persontableau, toute information sur le fruit dans le fruitstableau et toute information spécifique sur la relation entre une personne en particulier et un fruit particulier dans le person_fruittableau. Étant donné que dans cet exemple, il n'y a pas d'informations supplémentaires, la person_fruittable ne comporte que deux colonnes, les clés primaires des tables personet fruits. La quantité d'un fruit spécifique est cependant un exemple d'autre chose qui pourrait figurer dans le person_fruittableau.
Bad Wolf
2
Ne serait - il pas préférable d'utiliser un INTpour une clé fruitset que cette INTen person_fruit? Ainsi, le nom peut être changé plus tard et aurait également besoin de moins d'espace si vous n'avez pas beaucoup plus de lignes dans fruitsque dans person_fruit.
12431234123412341234123
59

La raison pour laquelle il n'y a pas de tableaux en SQL est que la plupart des gens n'en ont pas vraiment besoin. Les bases de données relationnelles (SQL est exactement cela) fonctionnent à l'aide de relations, et la plupart du temps, il est préférable d'attribuer une ligne d'une table à chaque "bit d'information". Par exemple, là où vous pouvez penser "Je voudrais une liste de choses ici", créez plutôt un nouveau tableau, en liant la ligne d'une table à la ligne d'une autre table. [1] De cette façon, vous pouvez représenter les relations M: N. Un autre avantage est que ces liens n'encombreront pas la ligne contenant l'élément lié. Et la base de données peut indexer ces lignes. Les tableaux ne sont généralement pas indexés.

Si vous n'avez pas besoin de bases de données relationnelles, vous pouvez utiliser par exemple un magasin clé-valeur.

En savoir plus sur la normalisation de la base de données , s'il vous plaît. La règle d'or est "[Chaque] [attribut] non clé doit fournir un fait sur la clé, la clé entière et rien d'autre que la clé.". Un tableau fait trop. Il a plusieurs faits et il stocke l'ordre (qui n'est pas lié à la relation elle-même). Et les performances sont médiocres (voir ci-dessus).

Imaginez que vous ayez une table de personne et une table avec des appels téléphoniques de personnes. Vous pouvez maintenant faire en sorte que chaque ligne de personne ait une liste de ses appels téléphoniques. Mais chaque personne a beaucoup d'autres relations avec beaucoup d'autres choses. Cela signifie-t-il que ma table person doit contenir un tableau pour chaque élément auquel il est connecté? Non, ce n'est pas un attribut de la personne elle-même.

[1]: Ce n'est pas grave si la table de liaison n'a que deux colonnes (les clés primaires de chaque table)! Si la relation elle-même a des attributs supplémentaires, ils doivent être représentés dans ce tableau sous forme de colonnes.

Janus Troelsen
la source
2
Merci Janus. Ça a du sens. Maintenant, je comprends pourquoi MySQL ne prend pas en charge le type de tableau dans une colonne.
tonga
2
@Sai - Pour ce que je fais, ai-je vraiment besoin de la solution NoSQL?
tonga
1
OK, donc si j'ai une table dans laquelle un champ contient un tableau numérique de milliers d'éléments, par exemple, des données 2D collectées à partir d'un capteur, est-il préférable d'utiliser NoSQL DB?
tonga
5
@tonga: La quantité de données ne détermine pas le type de base de données à utiliser, mais la nature des données. S'il n'y a pas de relations, vous n'avez pas besoin d'une base de données relationnelle. Mais comme il s'agit de la norme de l'industrie, vous pouvez la conserver et ne pas utiliser les fonctionnalités relationnelles. La plupart des données sont en quelque sorte relationnelles! Une raison courante pour dénormaliser des bases de données relationnelles ou utiliser des magasins de valeurs-clés est des raisons de performances. Mais ces problèmes ne surviennent qu'une fois que vous avez des MILLIONS de lignes! N'optimisez pas prématurément! Je recommanderais d'aller simplement avec une base de données SQL (je recommande PostgreSQL). Si vous avez des problèmes, demandez.
Janus Troelsen
2
PostgreSQL dispose également de magasins de valeurs clés intégrés, ce qui signifie qu'il serait encore plus facile de s'éloigner du modèle relationnel s'il ne vous convient pas.
Janus Troelsen
50

MySQL 5.7 fournit désormais un type de données JSON . Ce nouveau type de données offre une nouvelle façon pratique de stocker des données complexes: listes, dictionnaires, etc.

Cela dit, les rrays ne mappent pas bien les bases de données, c'est pourquoi les cartes relationnelles objet peuvent être assez complexes. Historiquement, les gens ont stocké des listes / tableaux dans MySQL en créant une table qui les décrit et en ajoutant chaque valeur comme son propre enregistrement. Le tableau peut avoir seulement 2 ou 3 colonnes, ou il peut en contenir beaucoup plus. La manière dont vous stockez ce type de données dépend vraiment des caractéristiques des données.

Par exemple, la liste contient-elle un nombre statique ou dynamique d'entrées? La liste restera-t-elle petite ou devrait-elle atteindre des millions d'enregistrements? Y aura-t-il beaucoup de lectures sur cette table? Beaucoup d'écritures? Beaucoup de mises à jour? Ce sont tous des facteurs dont il faut tenir compte pour décider de la manière de stocker les collections de données.

En outre, les magasins de données Key: Value / les magasins de documents tels que Cassandra, MongoDB, Redis, etc. fournissent également une bonne solution. Soyez juste conscient de l'endroit où les données sont réellement stockées (si elles sont stockées sur disque ou en mémoire). Toutes vos données ne doivent pas nécessairement être dans la même base de données. Certaines données ne correspondent pas bien à une base de données relationnelle et vous pouvez avoir des raisons de les stocker ailleurs, ou vous pouvez utiliser une clé en mémoire: base de données de valeurs comme hot-cache pour les données stockées sur disque quelque part ou comme stockage éphémère pour des choses comme des sessions.

Charles Addis
la source
43

Remarque à prendre en compte, vous pouvez stocker des tableaux dans Postgres.

Eric Grotke
la source
6
Remarque supplémentaire: ils peuvent être indexés, donc les requêtes vérifiant l'existence de valeurs spécifiques dans un tableau peuvent être très rapides. Il en va de même pour les types JSON complexes.
horaire du
5
Cela ne répond en aucun cas à la question. OP a posé des questions sur MySQL.
jhpratt
1
Si vous utilisez ArrayField dans Postgres et que vous disposez d'une liste exhaustive de valeurs dans cette colonne (comme une liste fixe de balises), vous pouvez créer un index GIN - cela accélérera considérablement les requêtes sur cette colonne.
lumos42
25

Dans MySQL, utilisez le type JSON.

Contre les réponses ci-dessus, la norme SQL inclut des types de tableaux depuis près de vingt ans; ils sont utiles, même si MySQL ne les a pas implémentés.

Dans votre exemple, cependant, vous voudrez probablement créer trois tables: person et fruit, puis person_fruit pour les rejoindre.

DROP TABLE IF EXISTS person_fruit;
DROP TABLE IF EXISTS person;
DROP TABLE IF EXISTS fruit;

CREATE TABLE person (
  person_id   INT           NOT NULL AUTO_INCREMENT,
  person_name VARCHAR(1000) NOT NULL,
  PRIMARY KEY (person_id)
);

CREATE TABLE fruit (
  fruit_id    INT           NOT NULL AUTO_INCREMENT,
  fruit_name  VARCHAR(1000) NOT NULL,
  fruit_color VARCHAR(1000) NOT NULL,
  fruit_price INT           NOT NULL,
  PRIMARY KEY (fruit_id)
);

CREATE TABLE person_fruit (
  pf_id     INT NOT NULL AUTO_INCREMENT,
  pf_person INT NOT NULL,
  pf_fruit  INT NOT NULL,
  PRIMARY KEY (pf_id),
  FOREIGN KEY (pf_person) REFERENCES person (person_id),
  FOREIGN KEY (pf_fruit) REFERENCES fruit (fruit_id)
);

INSERT INTO person (person_name)
VALUES
  ('John'),
  ('Mary'),
  ('John'); -- again

INSERT INTO fruit (fruit_name, fruit_color, fruit_price)
VALUES
  ('apple', 'red', 1),
  ('orange', 'orange', 2),
  ('pineapple', 'yellow', 3);

INSERT INTO person_fruit (pf_person, pf_fruit)
VALUES
  (1, 1),
  (1, 2),
  (2, 2),
  (2, 3),
  (3, 1),
  (3, 2),
  (3, 3);

Si vous souhaitez associer la personne à un éventail de fruits, vous pouvez le faire avec une vue:

DROP VIEW IF EXISTS person_fruit_summary;
CREATE VIEW person_fruit_summary AS
  SELECT
    person_id                                                                                              AS pfs_person_id,
    max(person_name)                                                                                       AS pfs_person_name,
    cast(concat('[', group_concat(json_quote(fruit_name) ORDER BY fruit_name SEPARATOR ','), ']') as json) AS pfs_fruit_name_array
  FROM
    person
    INNER JOIN person_fruit
      ON person.person_id = person_fruit.pf_person
    INNER JOIN fruit
      ON person_fruit.pf_fruit = fruit.fruit_id
  GROUP BY
    person_id;

La vue montre les données suivantes:

+---------------+-----------------+----------------------------------+
| pfs_person_id | pfs_person_name | pfs_fruit_name_array             |
+---------------+-----------------+----------------------------------+
|             1 | John            | ["apple", "orange"]              |
|             2 | Mary            | ["orange", "pineapple"]          |
|             3 | John            | ["apple", "orange", "pineapple"] |
+---------------+-----------------+----------------------------------+

Dans 5.7.22, vous voudrez utiliser JSON_ARRAYAGG , plutôt que de pirater le tableau ensemble à partir d'une chaîne.

a dessiné
la source
2

Utilisez le type de champ de base de données BLOB pour stocker les tableaux.

Réf: http://us.php.net/manual/en/function.serialize.php

Valeurs de retour

Renvoie une chaîne contenant une représentation par flux d'octets de la valeur qui peut être stockée n'importe où.

Notez qu'il s'agit d'une chaîne binaire qui peut inclure des octets nuls, et doit être stockée et gérée comme telle. Par exemple, la sortie serialize () doit généralement être stockée dans un champ BLOB dans une base de données, plutôt que dans un champ CHAR ou TEXT.

webdevfreak
la source
-4

vous pouvez stocker votre tableau en utilisant group_Concat comme ça

 INSERT into Table1 (fruits)  (SELECT GROUP_CONCAT(fruit_name) from table2)
 WHERE ..... //your clause here

ICI un exemple en violon

echo_Me
la source
4
Pas bien expliqué. Noms de table incorrects.
Martin F