วิธีการจัดเก็บอาร์เรย์ใน MySQL


119

ฉันมีสองตารางใน MySQL Table Person มีคอลัมน์ต่อไปนี้:

id | name | fruits

fruitsคอลัมน์อาจถือโมฆะหรืออาร์เรย์ของสตริงเช่น ( 'แอปเปิ้ล' 'ส้ม' 'กล้วย') หรือ ( 'สตรอเบอร์รี่') ฯลฯ ตารางที่สองเป็นตารางและผลไม้มีดังต่อไปนี้สามคอลัมน์นี้:

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

ดังนั้นฉันจะออกแบบfruitsคอลัมน์ในตารางแรกได้อย่างไรเพื่อให้สามารถเก็บอาร์เรย์ของสตริงที่รับค่าจากfruit_nameคอลัมน์ในตารางที่สองได้ เนื่องจากไม่มีประเภทข้อมูลอาร์เรย์ใน MySQL ฉันควรทำอย่างไร?



1
วิธีการเพิ่มเป็นรายการแยกต่างหากสีส้ม 2 1 กุหลาบ 2 1 ฯลฯ จากนั้นคุณสามารถใช้แบบสอบถามเพื่อปฏิบัติกับพวกเขาราวกับว่าเป็นอาร์เรย์
สาย

@JanusTroelsen: ฉันไม่ได้ใช้ PHP เพื่ออ่าน / เขียน DB มีวิธีที่เป็นสากลหรือไม่?
tonga

1
@tonga ตรวจสอบซอของฉันคือสิ่งที่คุณต้องการ?
echo_Me

คำตอบ:


164

วิธีที่เหมาะสมในการทำเช่นนี้คือการใช้ตารางหลายตารางและJOINในแบบสอบถามของคุณ

ตัวอย่างเช่น:

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_fruitตารางมีหนึ่งแถวสำหรับผลไม้ที่เป็นบุคคลที่เกี่ยวข้องกับแต่ละอย่างมีประสิทธิภาพและการเชื่อมโยงpersonและfruitsตารางด้วยกัน IE

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

เมื่อคุณต้องการดึงข้อมูลบุคคลและผลไม้ทั้งหมดของพวกเขาคุณสามารถทำสิ่งนี้ได้:

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
ตารางที่สามคือตารางเชื่อมโยงระหว่างบุคคลและผลไม้ ดังนั้นถ้าคนเรามีผลไม้ 100 ชิ้น ฉันต้องการสร้าง 100 แถวในตารางที่สามใช่ไหม มีประสิทธิภาพหรือไม่?
tonga

1
@tonga ว่าแต่ละ 100 แถวจะมีเหมือนกันแต่ที่แตกต่างกันperson_id fruit_nameนี่คือการนำทฤษฎีจากคำตอบของเจนัสไปใช้อย่างมีประสิทธิผล
Bad Wolf

1
เป็นความจริงเสมอไปหรือไม่ที่ความสัมพันธ์ระหว่างสองตารางจะต้องถูกเก็บไว้ในตารางที่สาม? ฉันสามารถทำแบบสอบถามเพื่อค้นหาความสัมพันธ์โดยเก็บคีย์หลักจากสองตารางได้หรือไม่
tonga

2
ใช่ซึ่งเป็นตัวอย่างในตอนนี้ ข้อมูลใด ๆ เกี่ยวกับบุคคลนั้นควรอยู่ในpersonตารางข้อมูลใด ๆ เกี่ยวกับผลไม้ในfruitsตารางและข้อมูลใด ๆ โดยเฉพาะเกี่ยวกับความสัมพันธ์ระหว่างบุคคลใดบุคคลหนึ่งกับผลไม้ชนิดใดชนิดหนึ่งในperson_fruitตาราง เนื่องจากในตัวอย่างนี้ไม่มีข้อมูลเพิ่มเติมใด ๆperson_fruitตารางจึงมีเพียงสองคอลัมน์คือคีย์หลักของตารางpersonและ fruitsปริมาณของผลไม้ที่เฉพาะเจาะจงเป็นตัวอย่างของสิ่งอื่นที่สามารถอยู่ในperson_fruitตารางได้
Bad Wolf

2
มันจะไม่ดีกว่าที่จะใช้INTสำหรับคีย์ในfruitsและมีเพียงแค่นี้INTในperson_fruit? ดังนั้นชื่อสามารถเปลี่ยนแปลงได้ในภายหลังและยังจะต้องมีพื้นที่ว่างน้อยลงหากคุณมีแถวไม่ได้อื่น ๆ อีกมากมายในกว่าในfruits person_fruit
12431234123412341234123

59

สาเหตุที่ไม่มีอาร์เรย์ใน SQL เป็นเพราะคนส่วนใหญ่ไม่ต้องการมันจริงๆ ฐานข้อมูลเชิงสัมพันธ์ (SQL คือสิ่งนั้น) ทำงานโดยใช้ความสัมพันธ์และโดยส่วนใหญ่แล้วจะเป็นการดีที่สุดหากคุณกำหนดหนึ่งแถวของตารางให้กับ "ข้อมูลบิต" แต่ละรายการ ตัวอย่างเช่นคุณอาจคิดว่า "ฉันต้องการรายการที่นี่" ให้สร้างตารางใหม่แทนโดยเชื่อมโยงแถวในตารางหนึ่งกับแถวในตารางอื่น [1] ด้วยวิธีนี้คุณสามารถแสดงความสัมพันธ์แบบ M: N ได้ ข้อดีอีกอย่างคือลิงก์เหล่านั้นจะไม่เกะกะแถวที่มีรายการที่เชื่อมโยง และฐานข้อมูลสามารถจัดทำดัชนีแถวเหล่านั้นได้ โดยทั่วไปอาร์เรย์จะไม่ถูกจัดทำดัชนี

หากคุณไม่ต้องการฐานข้อมูลเชิงสัมพันธ์คุณสามารถใช้เช่นที่เก็บคีย์ - ค่า

อ่านข้อมูลเกี่ยวกับการฟื้นฟูฐานข้อมูลโปรด กฎทองคือ "[ทุก] ที่ไม่ใช่คีย์ [แอตทริบิวต์] ต้องระบุข้อเท็จจริงเกี่ยวกับคีย์คีย์ทั้งหมดและไม่มีอะไรเลยนอกจากคีย์" อาร์เรย์ทำมากเกินไป มีข้อเท็จจริงหลายประการและจัดเก็บคำสั่งซื้อ (ซึ่งไม่เกี่ยวข้องกับความสัมพันธ์นั้นเอง) และประสิทธิภาพไม่ดี (ดูด้านบน)

ลองนึกภาพว่าคุณมีโต๊ะคนหนึ่งและคุณมีโต๊ะที่มีคนคุยโทรศัพท์ ตอนนี้คุณสามารถทำให้แต่ละแถวมีรายการโทรศัพท์ของเขา แต่คนทุกคนมีความสัมพันธ์อื่น ๆ อีกมากมาย นั่นหมายความว่าตารางบุคคลของฉันควรมีอาร์เรย์สำหรับทุกสิ่งที่เขาเชื่อมต่ออยู่หรือไม่? ไม่นั่นไม่ใช่คุณลักษณะของบุคคลนั้นเอง

[1]: จะเป็นไรถ้าตารางการเชื่อมโยงมีเพียงสองคอลัมน์ (คีย์หลักจากแต่ละตาราง)! หากความสัมพันธ์นั้นมีแอตทริบิวต์เพิ่มเติมควรแสดงในตารางนี้เป็นคอลัมน์


2
ขอบคุณ Janus ที่สมเหตุสมผล ตอนนี้ฉันเข้าใจแล้วว่าทำไม MySQL ไม่รองรับประเภทอาร์เรย์ในคอลัมน์
tonga

2
@ Sai - สำหรับสิ่งที่ฉันกำลังทำฉันต้องการโซลูชัน NoSQL จริงหรือไม่?
tonga

1
โอเคถ้าฉันมีตารางในฟิลด์ที่มีอาร์เรย์ที่เป็นตัวเลขขององค์ประกอบนับพันเช่นข้อมูล 2D บางส่วนที่รวบรวมจากเซ็นเซอร์จะดีกว่าไหมหากใช้ NoSQL DB
tonga

5
@tonga: จำนวนข้อมูลไม่ได้กำหนดประเภทฐานข้อมูลที่จะใช้ลักษณะของข้อมูลเป็นเช่นนั้น หากไม่มีความสัมพันธ์คุณไม่จำเป็นต้องใช้ฐานข้อมูลเชิงสัมพันธ์ แต่เนื่องจากนี่เป็นมาตรฐานอุตสาหกรรมคุณจึงสามารถคงไว้และไม่ใช้คุณสมบัติเชิงสัมพันธ์ได้ ข้อมูลส่วนใหญ่มีความสัมพันธ์ไม่ทางใดก็ทางหนึ่ง! สาเหตุทั่วไปในการทำให้ฐานข้อมูลเชิงสัมพันธ์ผิดปกติหรือใช้ที่เก็บคีย์ - ค่าเป็นเพราะเหตุผลด้านประสิทธิภาพ แต่ปัญหาเหล่านั้นจะเกิดขึ้นเมื่อคุณมีแถวนับล้าน! อย่าเพิ่มประสิทธิภาพก่อนเวลาอันควร! ฉันขอแนะนำให้ไปกับฐานข้อมูล SQL (ฉันแนะนำ PostgreSQL) หากคุณมีปัญหาถาม
Janus Troelsen

2
PostgreSQL ยังมีที่เก็บคีย์ - ค่าในตัวซึ่งหมายความว่าการย้ายออกจากโมเดลเชิงสัมพันธ์จะง่ายยิ่งขึ้นหากไม่เหมาะกับคุณ
Janus Troelsen

50

MySQL 5.7 ในขณะนี้ยังมีJSON ชนิดข้อมูล ประเภทข้อมูลใหม่นี้เป็นวิธีใหม่ที่สะดวกในการจัดเก็บข้อมูลที่ซับซ้อน: รายการพจนานุกรม ฯลฯ

ที่กล่าวว่าเรย์ไม่ได้แมปฐานข้อมูลที่ดีซึ่งเป็นเหตุผลว่าทำไมแผนที่เชิงสัมพันธ์เชิงวัตถุจึงค่อนข้างซับซ้อน ในอดีตผู้คนได้จัดเก็บรายการ / อาร์เรย์ใน MySQL โดยการสร้างตารางที่อธิบายถึงสิ่งเหล่านี้และเพิ่มแต่ละค่าเป็นบันทึกของตัวเอง ตารางอาจมีเพียง 2 หรือ 3 คอลัมน์หรืออาจมีมากกว่านั้นอีกมากมาย วิธีการจัดเก็บข้อมูลประเภทนี้ขึ้นอยู่กับลักษณะของข้อมูล

ตัวอย่างเช่นรายการมีจำนวนรายการแบบคงที่หรือแบบไดนามิกหรือไม่ รายการจะมีขนาดเล็กหรือคาดว่าจะเพิ่มขึ้นเป็นหลายล้านรายการ? จะมีการอ่านจำนวนมากบนตารางนี้หรือไม่? เขียนเยอะไหม? อัปเดตมากมาย? สิ่งเหล่านี้เป็นปัจจัยทั้งหมดที่ต้องพิจารณาเมื่อตัดสินใจว่าจะจัดเก็บคอลเลคชันข้อมูลอย่างไร

นอกจากนี้คีย์: ที่เก็บข้อมูลค่า / ที่เก็บเอกสารเช่น Cassandra, MongoDB, Redis เป็นต้นก็เป็นทางออกที่ดีเช่นกัน โปรดทราบว่าข้อมูลถูกจัดเก็บไว้ที่ใดจริง (หากเก็บไว้ในดิสก์หรือในหน่วยความจำ) ข้อมูลทั้งหมดของคุณไม่จำเป็นต้องอยู่ในฐานข้อมูลเดียวกัน ข้อมูลบางอย่างไม่สามารถจับคู่กับฐานข้อมูลเชิงสัมพันธ์ได้ดีและคุณอาจมีเหตุผลในการจัดเก็บไว้ที่อื่นหรือคุณอาจต้องการใช้ฐานข้อมูลคีย์ในหน่วยความจำ: ค่าเป็นฮอตแคชสำหรับข้อมูลที่จัดเก็บในดิสก์ที่ใดที่หนึ่งหรือเป็นที่เก็บข้อมูลชั่วคราว สำหรับสิ่งต่างๆเช่นเซสชัน


43

สิ่งที่ต้องพิจารณาคุณสามารถจัดเก็บอาร์เรย์ใน Postgres


6
หมายเหตุเพิ่มเติม: สามารถสร้างดัชนีได้ดังนั้นการตรวจสอบการมีอยู่ของค่าเฉพาะในอาร์เรย์จึงทำได้เร็วมาก เช่นเดียวกันกับประเภท JSON ที่ซับซ้อน
เวลา

5
นี่ไม่ได้ตอบคำถาม แต่อย่างใด OP ถามเกี่ยวกับ MySQL
jhpratt

1
หากคุณใช้ ArrayField ใน Postgres และมีรายการค่าทั้งหมดในคอลัมน์นั้น (เช่นรายการแท็กคงที่) คุณสามารถสร้างดัชนี GIN ได้ซึ่งจะทำให้การสืบค้นในคอลัมน์นั้นเร็วขึ้นอย่างมาก
lumos42

25

ใน MySQL ใช้ประเภท JSON

ตรงกันข้ามกับคำตอบข้างต้นมาตรฐาน SQL ได้รวมประเภทอาร์เรย์มาเกือบยี่สิบปีแล้ว มีประโยชน์แม้ว่า MySQL จะไม่ได้ใช้งานก็ตาม

อย่างไรก็ตามในตัวอย่างของคุณคุณอาจต้องการสร้างตารางสามตาราง: คนและผลไม้จากนั้นจึงใช้ 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);

หากคุณต้องการเชื่อมโยงบุคคลกับผลไม้หลายชนิดคุณสามารถทำได้ด้วยมุมมอง:

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;

มุมมองแสดงข้อมูลต่อไปนี้:

+---------------+-----------------+----------------------------------+
| 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 คุณจะต้องใช้JSON_ARRAYAGGแทนที่จะแฮ็คอาร์เรย์ด้วยกันจากสตริง


2

ใช้ประเภทฟิลด์ฐานข้อมูล BLOB เพื่อจัดเก็บอาร์เรย์

อ้างอิง: http://us.php.net/manual/en/function.serialize.php

ส่งคืนค่า

ส่งคืนสตริงที่มีการแทนค่าไบต์สตรีมที่สามารถเก็บไว้ที่ใดก็ได้

โปรดทราบว่านี่เป็นสตริงไบนารีซึ่งอาจมีไบต์ว่างและจำเป็นต้องจัดเก็บและจัดการเช่นนี้ ตัวอย่างเช่นโดยทั่วไปเอาต์พุต serialize () ควรถูกเก็บไว้ในฟิลด์ BLOB ในฐานข้อมูลแทนที่จะเป็นฟิลด์ CHAR หรือ TEXT


-4

คุณสามารถจัดเก็บอาร์เรย์ของคุณโดยใช้ group_Concat เช่นนั้น

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

ดูตัวอย่างในซอ


4
อธิบายไม่ถูก. ชื่อตารางไม่ถูกต้อง
Martin F
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.