Wie speichere ich Arrays in MySQL?


118

Ich habe zwei Tabellen in MySQL. Tabelle Person hat die folgenden Spalten:

id | name | fruits

Die fruitsSpalte kann null oder eine Reihe von Zeichenfolgen wie ('Apfel', 'Orange', 'Banane') oder ('Erdbeere') usw. enthalten. Die zweite Tabelle ist Table Fruit und enthält die folgenden drei Spalten:

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

Wie soll ich die fruitsSpalte in der ersten Tabelle so gestalten, dass sie ein Array von Zeichenfolgen enthalten kann, die Werte aus der fruit_nameSpalte in der zweiten Tabelle übernehmen? Wie soll ich vorgehen, da es in MySQL keinen Array-Datentyp gibt?



1
Wie wäre es, wenn Sie es als separate Einträge hinzufügen, orange, 2, 1, rose, 2, 1 usw., und dann können Sie Abfragen verwenden, um sie so zu behandeln, als wären sie Arrays.
Sai

@ JanusTroelsen: Ich benutze PHP nicht zum Lesen / Schreiben von DB. Gibt es also einen universellen Weg, dies zu tun?
Tonga

1
@ Tonga überprüfen meine Geige ist das, was Sie wollen?
echo_Me

Antworten:


163

Der richtige Weg, dies zu tun, besteht darin, mehrere Tabellen und JOINdiese in Ihren Abfragen zu verwenden.

Beispielsweise:

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

Die person_fruitTabelle enthält eine Zeile für jede Frucht einer Person zugeordnet ist und verbindet effektiv das personund fruitsTabellen zusammen, IE

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

Wenn Sie eine Person und all ihre Früchte abrufen möchten, können Sie Folgendes tun:

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
Die dritte Tabelle ist die Verknüpfungstabelle zwischen Person und Obst. Also, wenn eine Person 100 Früchte hat. Ich muss 100 Zeilen in der dritten Tabelle erstellen, oder? Ist das effizient?
Tonga

1
@tonga Genau, jede der 100 Zeilen hätte das gleiche, person_idaber ein anderes fruit_name. Dies ist effektiv eine Implementierung der Theorie aus Janus 'Antwort.
Bad Wolf

1
Stimmt es immer, dass eine Beziehung zwischen zwei Tabellen in der dritten Tabelle gespeichert werden muss? Kann ich einfach eine Abfrage durchführen, um die Beziehung zu finden, indem ich nur die Primärschlüssel aus zwei Tabellen speichere?
Tonga

2
Ja, so wird das Beispiel jetzt eingerichtet. Alle Informationen über die Person sollten in der personTabelle enthalten sein, alle Informationen über die Früchte in der fruitsTabelle und alle Informationen speziell über die Beziehung zwischen einer bestimmten Person und einer bestimmten Frucht in der person_fruitTabelle. Da in diesem Beispiel keine zusätzlichen Informationen vorhanden sind, besteht die person_fruitTabelle nur aus zwei Spalten, den Primärschlüsseln der Tabellen personund fruits. Die Menge einer bestimmten Frucht ist ein Beispiel für etwas anderes, das jedoch in die person_fruitTabelle aufgenommen werden könnte.
Bad Wolf

2
Wäre es nicht besser, a INTfür einen Schlüssel zu verwenden fruitsund nur diesen INTzu haben person_fruit? Der Name kann also später geändert werden und würde auch weniger Platz benötigen, wenn Sie nicht viel mehr Zeilen fruitsals in haben person_fruit.
12431234123412341234123

58

Der Grund dafür, dass es in SQL keine Arrays gibt, ist, dass die meisten Leute es nicht wirklich brauchen. Relationale Datenbanken (SQL ist genau das) arbeiten mit Relationen. In den meisten Fällen ist es am besten, wenn Sie jedem "Informationsbit" eine Zeile einer Tabelle zuweisen. Wenn du zum Beispiel denkst "Ich möchte hier eine Liste mit Dingen", erstelle stattdessen eine neue Tabelle und verknüpfe die Zeile in einer Tabelle mit der Zeile in einer anderen Tabelle. [1] Auf diese Weise können Sie M: N-Beziehungen darstellen. Ein weiterer Vorteil ist, dass diese Links die Zeile mit dem verknüpften Element nicht überladen. Und die Datenbank kann diese Zeilen indizieren. Arrays sind normalerweise nicht indiziert.

Wenn Sie keine relationalen Datenbanken benötigen, können Sie z. B. einen Schlüsselwertspeicher verwenden.

Lesen Sie bitte mehr über die Datenbanknormalisierung . Die goldene Regel lautet: "[Jeder] Nichtschlüssel [Attribut] muss eine Tatsache über den Schlüssel, den gesamten Schlüssel und nichts als den Schlüssel enthalten." Ein Array macht zu viel. Es hat mehrere Fakten und speichert die Reihenfolge (die nicht mit der Beziehung selbst zusammenhängt). Und die Leistung ist schlecht (siehe oben).

Stellen Sie sich vor, Sie haben einen Personentisch und einen Tisch mit Telefonanrufen von Personen. Jetzt können Sie jeder Personenzeile eine Liste ihrer Telefonanrufe geben. Aber jeder Mensch hat viele andere Beziehungen zu vielen anderen Dingen. Bedeutet das, dass meine Personentabelle ein Array für jede einzelne Sache enthalten sollte, mit der er verbunden ist? Nein, das ist kein Attribut der Person selbst.

[1]: Es ist in Ordnung, wenn die Verknüpfungstabelle nur zwei Spalten enthält (die Primärschlüssel aus jeder Tabelle)! Wenn die Beziehung selbst zusätzliche Attribute hat, sollten sie in dieser Tabelle als Spalten dargestellt werden.


2
Danke Janus. Das macht Sinn. Jetzt verstehe ich, warum MySQL den Array-Typ in einer Spalte nicht unterstützt.
Tonga

2
@Sai - Brauche ich für die Dinge, die ich mache, wirklich die NoSQL-Lösung?
Tonga

1
OK, wenn ich eine Tabelle habe, in der ein Feld ein numerisches Array von Tausenden von Elementen enthält, z. B. einige von einem Sensor gesammelte 2D-Daten, ist es viel besser, NoSQL DB zu verwenden?
Tonga

5
@tonga: Die Datenmenge bestimmt nicht den zu verwendenden DB-Typ, sondern die Art der Daten. Wenn es keine Beziehungen gibt, benötigen Sie keine relationale Datenbank. Da dies jedoch der Industriestandard ist, können Sie ihn beibehalten und die relationalen Funktionen einfach nicht verwenden. Die meisten Daten sind in irgendeiner Weise relational! Ein häufiger Grund für die Denormalisierung relationaler Datenbanken oder die Verwendung von Schlüsselwertspeichern sind Leistungsgründe. Diese Probleme treten jedoch erst auf, wenn Sie MILLIONEN Zeilen haben! Nicht vorzeitig optimieren! Ich würde empfehlen, nur mit einer SQL-Datenbank zu arbeiten (ich empfehle PostgreSQL). Wenn Sie Probleme haben, fragen Sie.
Janus Troelsen

2
In PostgreSQL sind auch Schlüsselwertspeicher integriert, was bedeutet, dass es noch einfacher ist, sich vom relationalen Modell zu entfernen, wenn es nicht zu Ihnen passt.
Janus Troelsen

50

MySQL 5.7 bietet jetzt einen JSON-Datentyp . Dieser neue Datentyp bietet eine bequeme neue Möglichkeit zum Speichern komplexer Daten: Listen, Wörterbücher usw.

Allerdings ordnen Rrays Datenbanken nicht gut zu, weshalb objektrelationale Karten sehr komplex sein können. In der Vergangenheit haben Benutzer Listen / Arrays in MySQL gespeichert, indem sie eine Tabelle erstellt haben, die sie beschreibt, und jeden Wert als eigenen Datensatz hinzugefügt haben. Die Tabelle enthält möglicherweise nur 2 oder 3 Spalten oder viele weitere. Wie Sie diese Art von Daten speichern, hängt wirklich von den Eigenschaften der Daten ab.

Enthält die Liste beispielsweise eine statische oder dynamische Anzahl von Einträgen? Wird die Liste klein bleiben oder wird sie voraussichtlich auf Millionen von Datensätzen anwachsen? Wird es viele Lesungen auf dieser Tabelle geben? Viele schreibt? Viele Updates? Dies sind alles Faktoren, die bei der Entscheidung über die Speicherung von Datensammlungen berücksichtigt werden müssen.

Auch Key: Value-Datenspeicher / Dokumentenspeicher wie Cassandra, MongoDB, Redis usw. bieten eine gute Lösung. Beachten Sie nur, wo die Daten tatsächlich gespeichert werden (wenn sie auf der Festplatte oder im Speicher gespeichert sind). Nicht alle Ihre Daten müssen sich in derselben Datenbank befinden. Einige Daten lassen sich nicht gut auf eine relationale Datenbank abbilden, und Sie haben möglicherweise Gründe, sie an anderer Stelle zu speichern, oder Sie möchten eine speicherinterne Schlüssel-Wert-Datenbank als Hot-Cache für Daten verwenden, die irgendwo auf der Festplatte gespeichert sind, oder als kurzlebiger Speicher für Dinge wie Sitzungen.


42

Als Randnotiz sollten Sie Arrays in Postgres speichern.


6
Zusätzlicher Hinweis: Sie können indiziert werden, sodass Abfragen, die auf das Vorhandensein bestimmter Werte in einem Array prüfen, sehr schnell erfolgen können. Gleiches gilt für komplexe JSON-Typen.
Zeitplan

5
Dies beantwortet die Frage in keiner Weise. OP fragte nach MySQL.
Jhpratt

1
Wenn Sie ArrayField in Postgres verwenden und eine vollständige Liste von Werten in dieser Spalte haben (wie eine feste Liste von Tags), können Sie einen GIN-Index erstellen. Dies beschleunigt die Abfragen in dieser Spalte erheblich.
Lumos42

25

Verwenden Sie in MySQL den JSON-Typ.

Entgegen den obigen Antworten enthält der SQL-Standard seit fast zwanzig Jahren Array-Typen. Sie sind nützlich, auch wenn MySQL sie nicht implementiert hat.

In Ihrem Beispiel möchten Sie jedoch wahrscheinlich drei Tabellen erstellen: Person und Obst, dann person_fruit, um sie zu verbinden.

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

Wenn Sie die Person mit einer Reihe von Früchten verknüpfen möchten, können Sie dies mit einer Ansicht tun:

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;

Die Ansicht zeigt folgende Daten:

+---------------+-----------------+----------------------------------+
| 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 möchten Sie JSON_ARRAYAGG verwenden , anstatt das Array zusammen aus einer Zeichenfolge zu hacken.


2

Verwenden Sie den Datenbankfeldtyp BLOB, um Arrays zu speichern.

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

Rückgabewerte

Gibt eine Zeichenfolge zurück, die eine Byte-Stream-Darstellung des Werts enthält, der überall gespeichert werden kann.

Beachten Sie, dass dies eine binäre Zeichenfolge ist, die Null-Bytes enthalten kann und als solche gespeichert und behandelt werden muss. Beispielsweise sollte die Ausgabe von serialize () im Allgemeinen in einem BLOB-Feld in einer Datenbank und nicht in einem CHAR- oder TEXT-Feld gespeichert werden.


-4

Sie können Ihr Array mit group_Concat so speichern

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

HIER ein Beispiel in Geige


4
Nicht gut erklärt. Schlechte Tabellennamen.
Martin F
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.