Diziler MySQL'de nasıl saklanır?


119

MySQL'de iki tablom var. Tablo Kişisi aşağıdaki sütunlara sahiptir:

id | name | fruits

fruitsSütun boş ya da ( 'elma', 'turuncu', 'muz') ya da ( 'çilek'), vb ikinci tablo Tablo Meyve ve aşağıdaki üç alana sahiptir gibi şeritlerinin bir dizi tutabilir:

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

Öyleyse fruits, ilk tablodaki fruit_namesütunu, ikinci tablodaki sütundan değerler alan dizeler dizisini tutabilecek şekilde nasıl tasarlamalıyım ? MySQL'de dizi veri türü olmadığına göre bunu nasıl yapmalıyım?



1
turuncu, 2, 1, gül, 2, 1 gibi ayrı girişler olarak eklemeye ne dersiniz? Daha sonra sorguları dizilermiş gibi ele almak için kullanabilirsiniz.
Sai

@JanusTroelsen: DB'yi okumak / yazmak için PHP kullanmıyorum. Peki bunu yapmanın evrensel bir yolu var mı?
tonga

1
@tonga kemanımı kontrol et, istediğin bu mu?
echo_Me

Yanıtlar:


164

Bunu yapmanın doğru yolu JOIN, sorgularınızda birden çok tablo ve bunları kullanmaktır .

Örneğin:

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

person_fruitTablo bir kişi ile ilişkili olan her meyve için bir satır içerir ve etkili bir şekilde bağlar personve fruitsbirlikte IE tabloları

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

Bir kişiyi ve tüm meyvelerini almak istediğinizde, şöyle bir şey yapabilirsiniz:

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
Üçüncü tablo, Kişi ve Meyve arasındaki bağlantı tablosudur. Yani bir kişinin 100 meyvesi varsa. Üçüncü tabloda 100 satır oluşturmam gerekiyor, değil mi? Bu verimli mi?
tonga

1
@tonga Kesinlikle, 100 sıranın her biri aynı person_idama farklı olacaktır fruit_name. Bu, Janus'un cevabından hareketle teorinin etkin bir uygulamasıdır.
Bad Wolf

1
İki tablo arasındaki herhangi bir ilişkinin üçüncü tabloda saklanması gerektiği her zaman doğru mudur? Sadece iki tablodaki birincil anahtarları depolayarak ilişkiyi bulmak için bir sorgu yapabilir miyim?
tonga

2
Evet, şimdi örnek böyle kuruluyor. Kişi hakkında personherhangi bir bilgi, fruitstablodaki meyve hakkında herhangi bir bilgi ve tablodaki belirli bir kişi ile belirli bir meyve arasındaki ilişki hakkında özellikle herhangi bir bilgi olmalıdır person_fruit. Bu örnekte ek bilgi olmadığı için person_fruittablo yalnızca iki sütun, personve fruitstablolarının birincil anahtarlarıdır . Ancak belirli bir meyvenin miktarı, person_fruittabloda yer alabilecek başka bir şeye örnektir .
Bad Wolf

2
INTBir anahtar için a kullanıp fruitssadece bunun INTiçinde olması daha iyi olmaz mıydı person_fruit? Böylece, ad daha sonra değiştirilebilir ve içinde fruitsolduğundan daha fazla satırınız yoksa daha az alana ihtiyaç duyacaktır person_fruit.
12431234123412341234123

59

SQL'de dizi olmamasının nedeni, çoğu insanın buna gerçekten ihtiyaç duymamasıdır. İlişkisel veritabanları (SQL tam olarak budur) ilişkileri kullanarak çalışır ve çoğu zaman, her "bilgi bitine" bir tablonun bir satırını atamak en iyisidir. Örneğin, "Burada bir şeyler listesi istiyorum" diye düşündüğünüzde, bunun yerine bir tablodaki satırı başka bir tablodaki satıra bağlayarak yeni bir tablo oluşturun. [1] Bu şekilde, M: N ilişkilerini temsil edebilirsiniz. Diğer bir avantaj, bu bağlantıların bağlantılı öğeyi içeren satırı karıştırmamasıdır. Ve veritabanı bu satırları indeksleyebilir. Diziler genellikle dizine alınmaz.

İlişkisel veritabanlarına ihtiyacınız yoksa, örneğin bir anahtar-değer deposu kullanabilirsiniz.

Veritabanı normalleştirme hakkındaki bilgileri okuyun lütfen. Altın kural "[Her] anahtarsız [öznitelik] anahtar, tüm anahtar hakkında bir bilgi sağlamalıdır ve anahtardan başka hiçbir şey sağlamamalıdır." Bir dizi çok fazla şey yapar. Birden fazla olgusu vardır ve emri saklar (ilişkinin kendisiyle ilgili değildir). Ve performans zayıf (yukarıya bakın).

Bir kişi masanız olduğunu ve insanların telefon görüşmelerinin olduğu bir masanız olduğunu hayal edin. Şimdi, sıradaki her kişinin telefon görüşmelerinin bir listesini olmasını sağlayabilirsiniz. Ancak her insanın başka birçok şeyle birçok başka ilişkisi vardır. Bu, kişi masamın bağlı olduğu her şey için bir dizi içermesi gerektiği anlamına mı geliyor? Hayır, bu kişinin kendisinin bir özelliği değildir.

[1]: Bağlantı tablosunda yalnızca iki sütun varsa (her tablodaki birincil anahtarlar) sorun değil! İlişkinin kendisi ek niteliklere sahipse, bunlar bu tabloda sütunlar olarak gösterilmelidir.


2
Teşekkürler Janus. Mantıklı. Şimdi MySQL'in bir sütundaki dizi türünü neden desteklemediğini anlıyorum.
tonga

2
@Sai - Yaptığım şeyler için gerçekten NoSQL çözümüne ihtiyacım var mı?
tonga

1
Tamam, eğer bir alanın sayısal bir dizi binlerce öğe içerdiği bir tablom varsa, örneğin, bir sensörden toplanan bazı 2D veriler, NoSQL DB kullanmak çok daha iyi olur mu?
tonga

5
@tonga: Veri miktarı, kullanılacak db türünü belirlemez, verilerin doğası belirler. İlişki yoksa, ilişkisel veritabanına ihtiyacınız yoktur. Ancak bu endüstri standardı olduğundan, onu tutabilir ve ilişkisel özellikleri kullanmayabilirsiniz. Çoğu veri bir şekilde ilişkiseldir! İlişkisel veritabanlarını normalleştirmenin veya anahtar-değer depolarını kullanmanın yaygın bir nedeni performans nedenleridir. Ancak bu sorunlar yalnızca MİLYONLARCA satırınız olduğunda ortaya çıkar! Zamanından önce optimize etmeyin! Sadece bir SQL db ile gitmenizi öneririm (PostgreSQL'i öneririm). Sorun yaşarsanız sorun.
Janus Troelsen

2
PostgreSQL ayrıca yerleşik anahtar-değer depolarına sahiptir, bu da size uymuyorsa ilişkisel modelden uzaklaşmanın daha da kolay olacağı anlamına gelir.
Janus Troelsen

50

MySQL 5.7 artık bir JSON veri türü sağlıyor . Bu yeni veri türü, karmaşık verileri depolamak için kullanışlı ve yeni bir yol sağlar: listeler, sözlükler vb.

Bununla birlikte, rray'ler veritabanlarını iyi eşlemez, bu nedenle nesne-ilişkisel haritalar oldukça karmaşık olabilir. Geçmişte insanlar, listeleri / dizileri, onları tanımlayan bir tablo oluşturarak ve her bir değeri kendi kaydı olarak ekleyerek MySQL'de depoladılar. Tabloda yalnızca 2 veya 3 sütun olabilir veya çok daha fazlasını içerebilir. Bu tür verileri nasıl saklayacağınız gerçekten verilerin özelliklerine bağlıdır.

Örneğin, liste statik veya dinamik sayıda girdi içeriyor mu? Liste küçük kalacak mı yoksa milyonlarca rekora ulaşması mı bekleniyor? Bu masada çok fazla okuma olacak mı? Çok fazla yazı var mı? Çok fazla güncelleme var mı? Bunlar, veri koleksiyonlarının nasıl depolanacağına karar verirken dikkate alınması gereken faktörlerdir.

Ayrıca Cassandra, MongoDB, Redis gibi Anahtar: Değer veri depoları / Belge depoları da iyi bir çözüm sağlar. Sadece verilerin gerçekte nerede depolandığına dikkat edin (eğer diskte veya bellekte depolanıyorsa). Tüm verilerinizin aynı veritabanında olması gerekmez. Bazı veriler ilişkisel bir veritabanıyla iyi eşleşmez ve bunları başka bir yerde depolamak için nedenleriniz olabilir veya bir bellek içi anahtar kullanmak isteyebilirsiniz: bir yerde diskte depolanan veriler için bir hot-cache veya geçici bir depolama olarak değer veritabanı seanslar gibi şeyler için.


43

Dikkate alınması gereken bir not, dizileri Postgres'te saklayabilirsiniz.


6
Ek not: indekslenebilirler, bu nedenle bir dizide belirli değerlerin varlığını kontrol eden sorgular çok hızlı olabilir. Aynı şey karmaşık JSON türleri için de geçerli.
timetofly

5
Bu soruya hiçbir şekilde cevap vermiyor. OP, MySQL hakkında soru sordu.
jhpratt

1
Postgres'te ArrayField kullanıyorsanız ve bu sütunda kapsamlı bir değerler listesi varsa (sabit bir etiket listesi gibi), bir GIN dizini oluşturabilirsiniz - bu sütundaki sorguları önemli ölçüde hızlandıracaktır.
lumos42

25

MySQL'de JSON türünü kullanın.

Yukarıdaki cevapların aksine, SQL standardı neredeyse yirmi yıldır dizi türlerini içermektedir; MySQL onları uygulamasa bile kullanışlıdırlar.

Bununla birlikte, örneğinizde, muhtemelen üç masa oluşturmak isteyeceksiniz: kişi ve meyve, ardından onlara katılmak için person_fruit.

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

Kişiyi bir dizi meyveyle ilişkilendirmek isterseniz, bunu bir görünümle yapabilirsiniz:

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;

Görünüm aşağıdaki verileri gösterir:

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

5.7.22'de, diziyi bir dizeden birlikte kesmek yerine JSON_ARRAYAGG kullanmak isteyeceksiniz .


2

Dizileri depolamak için veritabanı alanı türü BLOB kullanın.

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

Dönen Değerler

Herhangi bir yerde depolanabilen değerin bayt akışı gösterimini içeren bir dize döndürür.

Bunun, boş baytlar içerebilen ve bu şekilde depolanması ve kullanılması gereken bir ikili dizge olduğuna dikkat edin. Örneğin, serialize () çıktısı genellikle CHAR veya TEXT alanı yerine veritabanındaki bir BLOB alanında saklanmalıdır.


-4

dizinizi group_Concat kullanarak bu şekilde depolayabilirsiniz

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

BURADA kemanla ilgili bir örnek


4
İyi açıklanmadı. Kötü tablo adları.
Martin F
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.