Bagaimana cara menyimpan array di MySQL?

118

Saya memiliki dua tabel di MySQL. Orang Tabel memiliki kolom berikut:

id | name | fruits

The fruitskolom dapat memegang null atau array dari string seperti ( 'apel', 'orange', 'pisang'), atau ( 'strawberry'), dll Tabel kedua adalah Tabel Buah dan memiliki tiga kolom berikut:

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

Jadi bagaimana cara mendesain fruitskolom di tabel pertama sehingga dapat menampung larik string yang mengambil nilai dari fruit_namekolom di tabel kedua? Karena tidak ada tipe data array di MySQL, bagaimana saya harus melakukannya?

tonga
sumber
1
bagaimana dengan menambahkannya sebagai entri terpisah, oranye, 2, 1, mawar, 2, 1, dll. dan kemudian Anda dapat menggunakan kueri untuk memperlakukannya seolah-olah itu adalah array.
Sai
@ JanusTroelsen: Saya tidak menggunakan PHP untuk membaca / menulis DB. Jadi, apakah ada cara universal untuk melakukannya?
tonga
1
@tonga periksa biola saya apakah itu yang Anda inginkan?
echo_Me

Jawaban:

163

Cara yang tepat untuk melakukannya adalah dengan menggunakan beberapa tabel dan tabel tersebut JOINdalam kueri Anda.

Sebagai contoh:

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

The person_fruittabel berisi satu baris untuk setiap buah seseorang terkait dengan dan efektif menghubungkan persondan fruitstabel bersama-sama, IE

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

Saat Anda ingin mendapatkan kembali seseorang dan semua buahnya, Anda dapat melakukan sesuatu seperti ini:

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
Serigala jahat
sumber
4
Tabel ketiga adalah tabel penghubung antara Person dan Fruit. Jadi jika seseorang memiliki 100 buah. Saya perlu membuat 100 baris di tabel ketiga, bukan? Apakah ini efisien?
tonga
1
@tonga Tepatnya, masing-masing dari 100 baris akan memiliki yang sama person_idtetapi berbeda fruit_name. Ini secara efektif merupakan implementasi teori dari jawaban Janus.
Bad Wolf
1
Apakah selalu benar bahwa setiap hubungan antara dua tabel perlu disimpan di tabel ketiga? Bisakah saya melakukan kueri untuk menemukan hubungan hanya dengan menyimpan kunci utama dari dua tabel?
tonga
2
Ya, begitulah contoh disetup sekarang. Setiap informasi tentang orang tersebut harus ada di persontabel, informasi apa pun tentang buah di fruitstabel, dan informasi apa pun secara khusus tentang hubungan antara orang tertentu dan buah tertentu di person_fruittabel. Karena dalam contoh ini tidak ada informasi tambahan, person_fruittabel hanya terdiri dari dua kolom, kunci utama dari persondan fruitstabel. Jumlah buah tertentu adalah contoh dari sesuatu yang lain yang bisa masuk dalam person_fruittabel.
Bad Wolf
2
Bukankah lebih baik menggunakan a INTuntuk kunci fruitsdan hanya menyimpannya INTdi dalam person_fruit? Jadi, nama dapat diubah nanti dan juga membutuhkan lebih sedikit ruang jika Anda tidak memiliki lebih banyak baris fruitsdaripada di person_fruit.
12431234123412341234123
58

Alasan tidak adanya array dalam SQL, adalah karena kebanyakan orang tidak terlalu membutuhkannya. Database relasional (SQL persis seperti itu) bekerja menggunakan relasi, dan sebagian besar waktu, yang terbaik adalah jika Anda menetapkan satu baris tabel untuk setiap "bit informasi". Misalnya, di mana Anda mungkin berpikir "Saya ingin daftar barang di sini", alih-alih buat tabel baru, tautkan baris dalam satu tabel dengan baris di tabel lain. [1] Dengan begitu, Anda dapat merepresentasikan hubungan M: N. Keuntungan lainnya adalah tautan tersebut tidak akan mengacaukan baris yang berisi item yang ditautkan. Dan database dapat mengindeks baris tersebut. Array biasanya tidak diindeks.

Jika Anda tidak memerlukan database relasional, Anda dapat menggunakan misalnya penyimpanan nilai kunci.

Baca tentang normalisasi databaseSilakan . Aturan emasnya adalah "[Setiap] non-kunci [atribut] harus memberikan fakta tentang kunci, seluruh kunci, dan tidak lain adalah kunci.". Sebuah array melakukan terlalu banyak hal. Ia memiliki banyak fakta dan menyimpan urutan (yang tidak terkait dengan relasinya itu sendiri). Dan kinerjanya buruk (lihat di atas).

Bayangkan Anda memiliki tabel orang dan Anda memiliki meja dengan panggilan telepon oleh orang-orang. Sekarang Anda dapat membuat setiap baris orang memiliki daftar panggilan teleponnya. Tetapi setiap orang memiliki banyak hubungan lain dengan banyak hal lainnya. Apakah itu berarti tabel person saya harus berisi larik untuk setiap hal yang terhubung dengannya? Tidak, itu bukanlah atribut dari orang itu sendiri.

[1]: Tidak masalah jika tabel tautan hanya memiliki dua kolom (kunci utama dari setiap tabel)! Jika hubungan itu sendiri memiliki atribut tambahan, mereka harus direpresentasikan dalam tabel ini sebagai kolom.

Janus Troelsen
sumber
2
Terima kasih Janus. Itu masuk akal. Sekarang saya mengerti mengapa MySQL tidak mendukung tipe array dalam kolom.
tonga
2
@Sai - Untuk hal-hal yang saya lakukan, apakah saya benar-benar memerlukan solusi NoSQL?
tonga
1
Oke, jadi jika saya memiliki tabel di mana kolom berisi array numerik dari ribuan elemen, misalnya, beberapa data 2D dikumpulkan dari sensor, apakah jauh lebih baik menggunakan NoSQL DB?
tonga
5
@tonga: Jumlah data tidak menentukan jenis db yang akan digunakan, sifat datanya menentukan. Jika tidak ada relasi, Anda tidak perlu di database relasional. Tetapi karena ini adalah standar industri, Anda boleh menyimpannya dan tidak menggunakan fitur relasional. Sebagian besar data bersifat relasional! Alasan umum untuk mendenormalisasi database relasional atau menggunakan penyimpanan nilai kunci adalah karena alasan performa. Tetapi masalah itu hanya muncul setelah Anda memiliki JUTAAN baris! Jangan mengoptimalkan terlalu dini! Saya akan merekomendasikan menggunakan SQL db (saya merekomendasikan PostgreSQL). Jika Anda memiliki masalah, tanyakan.
Janus Troelsen
2
PostgreSQL juga memiliki penyimpanan nilai kunci built-in, yang berarti akan lebih mudah untuk menjauh dari model relasional jika tidak cocok untuk Anda.
Janus Troelsen
50

MySQL 5.7 sekarang menyediakan tipe data JSON . Jenis data baru ini menyediakan cara baru yang nyaman untuk menyimpan data kompleks: daftar, kamus, dll.

Yang mengatakan, sinar tidak memetakan database dengan baik, itulah sebabnya peta relasional objek bisa sangat kompleks. Secara historis orang telah menyimpan daftar / array di MySQL dengan membuat tabel yang mendeskripsikannya dan menambahkan setiap nilai sebagai recordnya sendiri. Tabel mungkin hanya memiliki 2 atau 3 kolom, atau mungkin berisi lebih banyak lagi. Bagaimana Anda menyimpan jenis data ini sangat bergantung pada karakteristik datanya.

Misalnya, apakah daftar berisi sejumlah entri statis atau dinamis? Akankah daftarnya tetap kecil, atau diharapkan bertambah menjadi jutaan catatan? Apakah akan ada banyak bacaan di tabel ini? Banyak menulis? Banyak pembaruan? Ini semua adalah faktor yang perlu dipertimbangkan saat memutuskan bagaimana menyimpan kumpulan data.

Juga, Kunci: Penyimpanan data nilai / Penyimpanan dokumen seperti Cassandra, MongoDB, Redis dll juga memberikan solusi yang baik. Berhati-hatilah di mana sebenarnya data disimpan (jika disimpan di disk atau di memori). Tidak semua data Anda harus berada dalam database yang sama. Beberapa data tidak dipetakan dengan baik ke database relasional dan Anda mungkin memiliki alasan untuk menyimpannya di tempat lain, atau Anda mungkin ingin menggunakan kunci dalam memori: database nilai sebagai cache panas untuk data yang disimpan di disk di suatu tempat atau sebagai penyimpanan sementara untuk hal-hal seperti sesi.

Charles Addis
sumber
43

Catatan samping untuk dipertimbangkan, Anda dapat menyimpan array di Postgres.

Eric Grotke
sumber
6
Catatan tambahan: mereka bisa diindeks, jadi query yang memeriksa keberadaan nilai tertentu dalam sebuah array bisa sangat cepat. Hal yang sama berlaku untuk jenis JSON yang kompleks.
Jadwal tanggal
5
Ini tidak menjawab pertanyaan dengan cara apa pun. OP bertanya tentang MySQL.
jhpratt
1
Jika Anda menggunakan ArrayField di Postgres dan memiliki daftar nilai yang lengkap di kolom itu (seperti daftar tag tetap), Anda dapat membuat indeks GIN - ini akan secara dramatis mempercepat kueri di kolom itu.
lumos42
25

Di MySQL, gunakan tipe JSON.

Dibandingkan dengan jawaban di atas, standar SQL telah memasukkan tipe array selama hampir dua puluh tahun; berguna, meskipun MySQL belum menerapkannya.

Namun, dalam contoh Anda, Anda mungkin ingin membuat tiga tabel: orang dan buah, lalu person_fruit untuk bergabung dengan mereka.

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

Jika Anda ingin mengaitkan orang tersebut dengan sederetan buah-buahan, Anda dapat melakukannya dengan sebuah tampilan:

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;

Tampilan tersebut menunjukkan data berikut:

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

Di 5.7.22, Anda akan ingin menggunakan JSON_ARRAYAGG , daripada meretas array bersama-sama dari sebuah string.

menggambar
sumber
2

Gunakan tipe kolom database BLOB untuk menyimpan array.

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

Kembalikan Nilai

Mengembalikan string yang berisi representasi aliran byte dari nilai yang dapat disimpan di mana saja.

Perhatikan bahwa ini adalah string biner yang mungkin menyertakan byte nol, dan perlu disimpan serta ditangani seperti itu. Misalnya, output serialize () umumnya harus disimpan dalam kolom BLOB dalam database, bukan kolom CHAR atau TEXT.

webdevfreak
sumber
-4

Anda dapat menyimpan array Anda menggunakan group_Concat seperti itu

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

DI SINI contoh biola

echo_Me
sumber
4
Tidak dijelaskan dengan baik. Nama tabel yang buruk.
Martin F