Làm cách nào để lưu trữ mảng trong MySQL?


118

Tôi có hai bảng trong MySQL. Table Person có các cột sau:

id | name | fruits

Các fruitscột có thể giữ null hoặc một mảng các chuỗi tương tự ( 'táo', 'da cam', 'chuối'), hoặc ( 'dâu tây'), vv Các bảng thứ hai là Bảng Trái cây và có ba cột sau:

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

Vậy tôi nên thiết kế fruitscột trong bảng đầu tiên như thế nào để nó có thể chứa mảng chuỗi lấy giá trị từ fruit_namecột trong bảng thứ hai? Vì không có kiểu dữ liệu mảng trong MySQL, tôi phải làm như thế nào?



1
làm thế nào về việc thêm nó dưới dạng các mục nhập riêng biệt, cam, 2, 1, hoa hồng, 2, 1, v.v. và sau đó bạn có thể sử dụng các truy vấn để coi chúng như thể chúng là mảng.
Sai

@JanusTroelsen: Tôi không sử dụng PHP để đọc / ghi DB. Vì vậy, có một cách phổ quát để làm điều đó?
tonga

1
@tonga kiểm tra lại cái fiddle của tôi có phải là thứ bạn muốn không?
echo_Me

Câu trả lời:


163

Cách thích hợp để làm điều này là sử dụng nhiều bảng và JOINchúng trong các truy vấn của bạn.

Ví dụ:

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

Các person_fruitbảng chứa một hàng cho mỗi quả một người có liên quan đến hiệu quả và nối liền personfruitsbàn với nhau, IE

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

Khi bạn muốn lấy một người và tất cả trái cây của họ, bạn có thể làm như sau:

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
Bảng thứ ba là bảng liên kết giữa Người và Quả. Vì vậy, nếu một người có 100 quả. Tôi cần tạo 100 hàng trong bảng thứ ba, phải không? Điều này có hiệu quả không?
tonga

1
@tonga Chính xác, mỗi hàng trong số 100 hàng sẽ giống nhau person_idnhưng khác fruit_name. Đây thực sự là sự triển khai lý thuyết từ câu trả lời của Janus.
Bad Wolf,

1
Có phải luôn luôn đúng rằng bất kỳ quan hệ nào giữa hai bảng cần được lưu trữ trong bảng thứ ba không? Tôi có thể thực hiện một truy vấn để tìm mối quan hệ chỉ bằng cách lưu trữ các khóa chính từ hai bảng không?
tonga

2
Vâng, đó là cách ví dụ được thiết lập bây giờ. Mọi thông tin về người đó nên có trong personbảng, mọi thông tin về trái cây trong fruitsbàn và mọi thông tin cụ thể về mối quan hệ giữa một người cụ thể và một loại trái cây cụ thể trong person_fruitbàn. Vì trong ví dụ này không có bất kỳ thông tin bổ sung nào nên person_fruitbảng chỉ có hai cột, khóa chính của bảng personfruits. Tuy nhiên, số lượng của một loại trái cây cụ thể là một ví dụ về một số thứ khác có thể có trong person_fruitbảng.
Bad Wolf,

2
Sẽ không tốt hơn nếu sử dụng một INTcho một khóa trong fruitsvà chỉ có cái này INTtrong person_fruit? Vì vậy, tên có thể được thay đổi sau đó và cũng sẽ cần ít khoảng trống hơn nếu bạn không có nhiều hàng fruitshơn trong person_fruit.
12431234123412341234123

58

Lý do mà không có mảng nào trong SQL, là vì hầu hết mọi người không thực sự cần nó. Cơ sở dữ liệu quan hệ (chính xác là SQL) hoạt động bằng cách sử dụng quan hệ và hầu hết thời gian, tốt nhất là bạn chỉ định một hàng của bảng cho mỗi "bit thông tin". Ví dụ: khi bạn có thể nghĩ rằng "Tôi muốn có một danh sách ở đây", thay vào đó hãy tạo một bảng mới, liên kết hàng trong một bảng với hàng trong bảng khác. [1] Bằng cách đó, bạn có thể biểu diễn mối quan hệ M: N. Một ưu điểm khác là các liên kết đó sẽ không làm lộn xộn hàng chứa mục được liên kết. Và cơ sở dữ liệu có thể lập chỉ mục các hàng đó. Mảng thường không được lập chỉ mục.

Nếu bạn không cần cơ sở dữ liệu quan hệ, bạn có thể sử dụng ví dụ: kho khóa-giá trị.

Vui lòng đọc về chuẩn hóa cơ sở dữ liệu . Quy tắc vàng là "[Mọi] [thuộc tính] không phải khóa phải cung cấp thông tin về khóa, toàn bộ khóa và không có gì khác ngoài khóa.". Một mảng làm quá nhiều. Nó có nhiều dữ kiện và nó lưu trữ thứ tự (không liên quan đến bản thân mối quan hệ). Và hiệu suất kém (xem ở trên).

Hãy tưởng tượng rằng bạn có một bàn dành cho người và bạn có một bàn với những cuộc gọi điện thoại của mọi người. Bây giờ bạn có thể làm cho mỗi hàng người có một danh sách các cuộc gọi điện thoại của mình. Nhưng mỗi người có nhiều mối quan hệ khác đến nhiều thứ khác. Điều đó có nghĩa là bảng người của tôi phải chứa một mảng cho mọi thứ mà anh ta được kết nối? Không, đó không phải là một thuộc tính của chính con người.

[1]: Không sao nếu bảng liên kết chỉ có hai cột (khóa chính của mỗi bảng)! Tuy nhiên, nếu bản thân mối quan hệ có các thuộc tính bổ sung, chúng phải được biểu diễn trong bảng này dưới dạng cột.


2
Cảm ơn Janus. Điều đó có lý. Bây giờ tôi hiểu tại sao MySQL không hỗ trợ kiểu mảng trong một cột.
tonga

2
@Sai - Đối với những thứ tôi đang làm, tôi có thực sự cần giải pháp NoSQL không?
tonga

1
OK, vì vậy nếu tôi có một bảng trong đó một trường chứa một mảng số gồm hàng nghìn phần tử, chẳng hạn như một số dữ liệu 2D được thu thập từ một bộ cảm biến, thì liệu sử dụng NoSQL DB có tốt hơn nhiều không?
tonga

5
@tonga: Số lượng dữ liệu không xác định loại db sẽ sử dụng, bản chất của dữ liệu thì có. Nếu không có quan hệ, bạn không cần ở cơ sở dữ liệu quan hệ. Nhưng vì đây là tiêu chuẩn công nghiệp, bạn có thể giữ nó và không sử dụng các tính năng quan hệ. Hầu hết dữ liệu là quan hệ theo một cách nào đó! Một lý do phổ biến cho việc không chuẩn hóa cơ sở dữ liệu quan hệ hoặc sử dụng kho khóa-giá trị là vì lý do hiệu suất. Nhưng những vấn đề đó chỉ nảy sinh khi bạn có HÀNG TRIỆU hàng! Đừng tối ưu hóa quá sớm! Tôi khuyên bạn nên chỉ sử dụng SQL db (tôi khuyên bạn nên sử dụng PostgreSQL). Nếu bạn có vấn đề, hãy hỏi.
Janus Troelsen

2
PostgreSQL cũng được tích hợp sẵn các kho khóa-giá trị, có nghĩa là bạn sẽ dễ dàng di chuyển khỏi mô hình quan hệ hơn nếu nó không phù hợp với bạn.
Janus Troelsen

50

MySQL 5.7 hiện cung cấp kiểu dữ liệu JSON . Kiểu dữ liệu mới này cung cấp một cách mới thuận tiện để lưu trữ dữ liệu phức tạp: danh sách, từ điển, v.v.

Điều đó nói rằng, rrays không lập bản đồ cơ sở dữ liệu tốt, đó là lý do tại sao bản đồ quan hệ đối tượng có thể khá phức tạp. Trong lịch sử, mọi người đã lưu trữ danh sách / mảng trong MySQL bằng cách tạo một bảng mô tả chúng và thêm từng giá trị làm bản ghi của chính nó. Bảng có thể chỉ có 2 hoặc 3 cột hoặc có thể chứa nhiều cột khác. Cách bạn lưu trữ loại dữ liệu này thực sự phụ thuộc vào đặc điểm của dữ liệu.

Ví dụ, danh sách chứa một số mục nhập tĩnh hay động? Danh sách sẽ tiếp tục nhỏ hay dự kiến ​​sẽ tăng lên hàng triệu bản ghi? Sẽ có rất nhiều bài đọc trên bảng này? Viết rất nhiều? Nhiều cập nhật? Đây là tất cả các yếu tố cần được xem xét khi quyết định cách lưu trữ bộ sưu tập dữ liệu.

Ngoài ra, Khoá: Kho dữ liệu giá trị / Kho tài liệu như Cassandra, MongoDB, Redis, v.v. cũng cung cấp một giải pháp tốt. Chỉ cần lưu ý về nơi dữ liệu thực sự đang được lưu trữ (nếu dữ liệu được lưu trữ trên đĩa hoặc trong bộ nhớ). Không phải tất cả dữ liệu của bạn cần phải nằm trong cùng một cơ sở dữ liệu. Một số dữ liệu không liên kết tốt với cơ sở dữ liệu quan hệ và bạn có thể có lý do để lưu trữ nó ở nơi khác hoặc bạn có thể muốn sử dụng khóa trong bộ nhớ: cơ sở dữ liệu giá trị làm bộ đệm nóng cho dữ liệu được lưu trữ trên đĩa ở đâu đó hoặc như một bộ nhớ tạm thời cho những thứ như phiên.


42

Một chú thích phụ cần xem xét, bạn có thể lưu trữ các mảng trong Postgres.


6
Lưu ý thêm: chúng có thể được lập chỉ mục, vì vậy các truy vấn kiểm tra sự tồn tại của các giá trị cụ thể trong một mảng có thể rất nhanh. Tương tự với các loại JSON phức tạp.
timetofly

5
Điều này không trả lời câu hỏi theo bất kỳ cách nào. OP hỏi về MySQL.
jhpratt 30/07/19

1
Nếu bạn sử dụng ArrayField trong Postgres và có danh sách đầy đủ các giá trị trong cột đó (như danh sách thẻ cố định), bạn có thể tạo chỉ mục GIN - nó sẽ tăng tốc đáng kể các truy vấn trên cột đó.
lumos42

25

Trong MySQL, sử dụng kiểu JSON.

Ngược lại với các câu trả lời ở trên, tiêu chuẩn SQL đã bao gồm các kiểu mảng trong gần 20 năm; chúng hữu ích, ngay cả khi MySQL chưa triển khai chúng.

Tuy nhiên, trong ví dụ của bạn, bạn có thể sẽ muốn tạo ba bảng: người và trái cây, sau đó người_hỗ để tham gia chúng.

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

Nếu bạn muốn liên kết người đó với một mảng trái cây, bạn có thể làm như vậy với một dạng xem:

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;

Chế độ xem hiển thị dữ liệu sau:

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

Trong 5.7.22, bạn sẽ muốn sử dụng JSON_ARRAYAGG , thay vì hack mảng với nhau từ một chuỗi.


2

Sử dụng kiểu trường cơ sở dữ liệu BLOB để lưu trữ mảng.

Tham khảo: http://us.php.net/manual/en/ Chức năng.serialize.php

Giá trị trả lại

Trả về một chuỗi chứa biểu diễn luồng byte của giá trị có thể được lưu trữ ở bất kỳ đâu.

Lưu ý rằng đây là một chuỗi nhị phân có thể bao gồm các byte rỗng và cần được lưu trữ và xử lý như vậy. Ví dụ: đầu ra serialize () thường phải được lưu trữ trong trường BLOB trong cơ sở dữ liệu, chứ không phải là trường CHAR hoặc TEXT.


-4

bạn có thể lưu trữ mảng của mình bằng group_Concat như vậy

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

ĐÂY một ví dụ trong fiddle


4
Không được giải thích rõ. Tên bảng không hợp lệ.
Martin F
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.