Come memorizzare gli array in MySQL?


118

Ho due tabelle in MySQL. La tabella Persona ha le seguenti colonne:

id | name | fruits

La fruitscolonna può contenere null o un array di stringhe come ('apple', 'orange', 'banana') o ('strawberry'), ecc. La seconda tabella è Table Fruit e ha le seguenti tre colonne:

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

Quindi, come devo progettare la fruitscolonna nella prima tabella in modo che possa contenere un array di stringhe che prendono valori dalla fruit_namecolonna nella seconda tabella? Dato che non esiste un tipo di dati array in MySQL, come dovrei farlo?



1
che ne dici di aggiungerlo come voci separate, arancione, 2, 1, rosa, 2, 1, ecc. e poi puoi usare le query per trattarle come se fossero array.
Sai

@ JanusTroelsen: non sto usando PHP per leggere / scrivere DB. Quindi esiste un modo universale per farlo?
tonga

1
@tonga controlla il mio violino, è quello che vuoi?
echo_Me

Risposte:


163

Il modo corretto per farlo è usare più tabelle e JOINloro nelle tue query.

Per esempio:

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`)
);

La person_fruittabella contiene una riga per ogni frutto una persona è associata ed efficacemente collega la persone fruitstavoli insieme, IE

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

Quando vuoi recuperare una persona e tutti i suoi frutti puoi fare qualcosa del genere:

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

4
La terza tabella è la tabella dei collegamenti tra Person e Fruit. Quindi, se una persona ha 100 frutti. Devo creare 100 righe nella terza tabella, giusto? È efficiente?
tonga

1
@tonga Esattamente, ognuna delle 100 righe avrebbe la stessa person_idma una diversa fruit_name. Questa è effettivamente un'implementazione della teoria dalla risposta di Janus.
Bad Wolf

1
È sempre vero che qualsiasi relazione tra due tabelle deve essere memorizzata nella terza tabella? Posso semplicemente fare una query per trovare la relazione semplicemente memorizzando le chiavi primarie da due tabelle?
tonga

2
Sì, ecco come è configurato l'esempio ora. Qualsiasi informazione sulla persona dovrebbe essere nella persontabella, qualsiasi informazione sul frutto nella fruitstabella e qualsiasi informazione specifica sulla relazione tra una particolare persona e un particolare frutto nella person_fruittabella. Poiché in questo esempio non sono presenti informazioni aggiuntive, la person_fruittabella è composta solo da due colonne, le chiavi primarie delle tabelle persone fruits. La quantità di un frutto specifico è un esempio di qualcos'altro che potrebbe andare in person_fruittavola.
Bad Wolf

2
Non sarebbe meglio usare una INTper una chiave in fruitse hanno solo questo INTin person_fruit? Quindi il nome può essere modificato in seguito e richiederebbe anche meno spazio se non ci sono molte più righe fruitsin person_fruit.
12431234123412341234123

58

Il motivo per cui non ci sono array in SQL è perché la maggior parte delle persone non ne ha davvero bisogno. I database relazionali (SQL è esattamente questo) funzionano utilizzando le relazioni e, il più delle volte, è meglio se si assegna una riga di una tabella a ciascun "bit di informazione". Ad esempio, dove potresti pensare "Vorrei un elenco di cose qui", crea invece una nuova tabella, collegando la riga in una tabella con la riga in un'altra tabella. [1] In questo modo, puoi rappresentare relazioni M: N. Un altro vantaggio è che quei collegamenti non ingombreranno la riga contenente l'elemento collegato. E il database può indicizzare quelle righe. Gli array in genere non sono indicizzati.

Se non hai bisogno di database relazionali, puoi usare ad esempio un archivio di valori-chiave.

Leggi informazioni sulla normalizzazione del database , per favore. La regola d'oro è "[Ogni] [attributo] non chiave deve fornire un fatto sulla chiave, l'intera chiave e nient'altro che la chiave.". Un array fa troppo. Ha più fatti e memorizza l'ordine (che non è correlato alla relazione stessa). E le prestazioni sono scadenti (vedi sopra).

Immagina di avere un tavolo per le persone e un tavolo con le telefonate delle persone. Ora puoi fare in modo che ogni riga di persona abbia un elenco delle sue telefonate. Ma ogni persona ha molte altre relazioni con molte altre cose. Ciò significa che la mia tabella person dovrebbe contenere un array per ogni singola cosa a cui è connesso? No, questo non è un attributo della persona stessa.

[1]: Va bene se la tabella di collegamento ha solo due colonne (le chiavi primarie di ciascuna tabella)! Se la relazione stessa ha attributi aggiuntivi, tuttavia, dovrebbero essere rappresentati in questa tabella come colonne.


2
Grazie Janus. Questo ha senso. Ora capisco perché MySQL non supporta il tipo di array in una colonna.
tonga

2
@Sai - Per le cose che sto facendo, ho davvero bisogno della soluzione NoSQL?
tonga

1
OK, quindi se ho una tabella in cui un campo contiene una matrice numerica di migliaia di elementi, ad esempio alcuni dati 2D raccolti da un sensore, è molto meglio usare NoSQL DB?
tonga

5
@tonga: la quantità di dati non determina il tipo di database da utilizzare, la natura dei dati sì. Se non ci sono relazioni, non è necessario un database relazionale. Ma poiché questo è lo standard del settore, è possibile mantenerlo e non utilizzare le funzionalità relazionali. La maggior parte dei dati è in qualche modo relazionale! Un motivo comune per denormalizzare i database relazionali o utilizzare archivi di valori-chiave è dovuto a motivi di prestazioni. Ma questi problemi sorgono solo quando hai MILIONI di file! Non ottimizzare prematuramente! Consiglierei di andare semplicemente con un database SQL (consiglio PostgreSQL). Se hai problemi, chiedi.
Janus Troelsen

2
PostgreSQL ha anche archivi di valori-chiave integrati, il che significa che sarebbe ancora più facile allontanarsi dal modello relazionale se non ti si adatta.
Janus Troelsen

50

MySQL 5.7 ora fornisce un tipo di dati JSON . Questo nuovo tipo di dati fornisce un nuovo modo conveniente per memorizzare dati complessi: elenchi, dizionari, ecc.

Detto questo, i rray non mappano bene i database, motivo per cui le mappe relazionali a oggetti possono essere piuttosto complesse. Storicamente le persone hanno memorizzato elenchi / array in MySQL creando una tabella che li descrive e aggiungendo ogni valore come proprio record. La tabella può avere solo 2 o 3 colonne o può contenerne molte di più. Il modo in cui memorizzi questo tipo di dati dipende in realtà dalle caratteristiche dei dati.

Ad esempio, l'elenco contiene un numero di voci statico o dinamico? L'elenco rimarrà piccolo o dovrebbe crescere fino a milioni di record? Ci saranno molte letture su questa tabella? Molte scritture? Molti aggiornamenti? Questi sono tutti fattori che devono essere considerati quando si decide come memorizzare le raccolte di dati.

Inoltre, anche gli archivi dati chiave: valore / archivi di documenti come Cassandra, MongoDB, Redis ecc. Forniscono una buona soluzione. Basta essere consapevoli di dove i dati vengono effettivamente archiviati (se vengono archiviati su disco o in memoria). Non tutti i tuoi dati devono essere nello stesso database. Alcuni dati non vengono mappati bene a un database relazionale e potresti avere motivi per archiviarli altrove, oppure potresti voler utilizzare una chiave in memoria: il database del valore come hot-cache per i dati archiviati su disco da qualche parte o come memoria temporanea per cose come le sessioni.


43

Una nota a margine da considerare, puoi memorizzare gli array in Postgres.


6
Nota aggiuntiva: possono essere indicizzati, quindi le query che verificano l'esistenza di valori specifici in un array possono essere molto veloci. Lo stesso vale per i tipi JSON complessi.
orario

5
Questo non risponde in alcun modo alla domanda. OP ha chiesto informazioni su MySQL.
jhpratt

1
Se utilizzi ArrayField in Postgres e disponi di un elenco esaustivo di valori in quella colonna (come un elenco fisso di tag), puoi creare un indice GIN: accelererà notevolmente le query su quella colonna.
lumos42

25

In MySQL, utilizza il tipo JSON.

Contrariamente alle risposte precedenti, lo standard SQL include i tipi di array da quasi vent'anni; sono utili, anche se MySQL non li ha implementati.

Nel tuo esempio, tuttavia, probabilmente vorrai creare tre tabelle: person e fruit, quindi person_fruit per unirsi a loro.

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);

Se desideri associare la persona a una serie di frutti, puoi farlo con una vista:

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 vista mostra i seguenti dati:

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

In 5.7.22, ti consigliamo di utilizzare JSON_ARRAYAGG , piuttosto che hackerare l'array insieme da una stringa.


2

Utilizzare il tipo di campo del database BLOB per archiviare gli array.

Rif: http://us.php.net/manual/en/function.serialize.php

Valori restituiti

Restituisce una stringa contenente una rappresentazione del flusso di byte del valore che può essere archiviata ovunque.

Si noti che questa è una stringa binaria che può includere byte nulli e deve essere archiviata e gestita come tale. Ad esempio, l'output di serialize () dovrebbe generalmente essere archiviato in un campo BLOB in un database, piuttosto che in un campo CHAR o TEXT.


-4

puoi memorizzare il tuo array usando group_Concat in questo modo

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

QUI un esempio in violino


4
Non ben spiegato. Nomi di tabella errati.
Martin F
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.