173

我在 MySQL 中有两个表。表人具有以下列:

ID 姓名 水果

fruits列可能包含 null 或字符串数​​组,如 ('apple', 'orange', 'banana') 或 ('strawberry') 等。第二个表是 Table Fruit 并具有以下三列:

水果名称 颜色 价格
苹果 红色的 2
3
------------ -------- ------

那么我应该如何设计fruits第一个表中的列,以便它可以保存从fruit_name第二个表中的列中获取值的字符串数组?既然MySQL中没有数组数据类型,那我该怎么办呢?

4

7 回答 7

212

正确的方法是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表包含与一个人相关联的每个水果的一行,并将表personfruits表有效地链接在一起,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
于 2013-06-28T18:55:08.810 回答
73

SQL 中没有数组的原因是因为大多数人并不真正需要它。关系数据库(SQL 就是这样)使用关系工作,而且大多数情况下,最好将表的一行分配给每个“信息位”。例如,您可能会认为“我想要这里的东西列表”,而是创建一个新表,将一个表中的行与另一个表中的行链接。[1] 这样,您可以表示 M:N 关系。另一个优点是这些链接不会弄乱包含链接项目的行。并且数据库可以索引这些行。数组通常没有索引。

如果您不需要关系数据库,您可以使用例如键值存储。

请阅读有关数据库规范化的信息。黄金法则是“[每个] 非键 [属性] 必须提供关于键、整个键的事实,并且只提供键。”。数组做的太多了。它有多个事实并存储顺序(与关系本身无关)。而且性能很差(见上文)。

想象一下,您有一张人桌,并且您有一张有人打电话的桌子。现在你可以让每个人的行都有一个他的电话列表。但是每个人与许多其他事物都有许多其他关系。这是否意味着我的 person 表应该为他所连接的每一件事都包含一个数组?不,这不是人本身的属性。

[1]:如果链接表只有两列(每个表的主键)也没关系!但是,如果关系本身具有其他属性,则它们应在此表中表示为列。

于 2013-06-28T18:59:02.640 回答
60

MySQL 5.7 现在提供JSON 数据类型。这种新的数据类型提供了一种方便的新方式来存储复杂数据:列表、字典等。

也就是说,数组不能很好地映射数据库,这就是对象关系映射可能非常复杂的原因。历史上,人们通过创建一个描述它们的表并将每个值添加为自己的记录来在 MySQL 中存储列表/数组。该表可能只有 2 或 3 列,也可能包含更多。如何存储此类数据实际上取决于数据的特征。

例如,列表是否包含静态或动态数量的条目?该列表会保持很小,还是预计会增长到数百万条记录?这张桌子上会有很多读物吗?写的多吗?更新多吗?这些都是在决定如何存储数据集合时需要考虑的因素。

此外,Key/Value 数据存储、Cassandra、MongoDB、Redis 等文档存储也提供了很好的解决方案。只需注意数据的实际存储位置(如果它存储在磁盘或内存中)。并非所有数据都需要位于同一个数据库中。有些数据不能很好地映射到关系数据库,您可能有理由将其存储在其他地方,或者您可能希望使用内存中的键:值数据库作为存储在某处磁盘上的数据的热缓存或作为临时存储对于会话之类的事情。

于 2013-06-28T20:01:55.900 回答
59

需要考虑的一个旁注,您可以在 Postgres 中存储数组。

于 2015-09-04T22:12:54.927 回答
45

在 MySQL 中,使用 JSON 类型。

与上面的答案相反,SQL 标准包含数组类型已有近 20 年了。它们很有用,即使 MySQL 没有实现它们。

但是,在您的示例中,您可能希望创建三个表:person 和fruit,然后将 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,而不是从字符串中将数组组合在一起。

于 2018-03-03T21:06:37.963 回答
0

使用数据库字段类型 BLOB 来存储数组。

参考:http ://us.php.net/manual/en/function.serialize.php

返回值

返回一个字符串,其中包含可以存储在任何地方的值的字节流表示形式。

请注意,这是一个可能包含空字节的二进制字符串,需要按原样存储和处理。例如,serialize() 输出通常应存储在数据库中的 BLOB 字段中,而不是 CHAR 或 TEXT 字段中。

于 2017-07-06T12:06:29.197 回答
-8

你可以像这样使用 group_Concat 来存储你的数组

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

这里是小提琴的一个例子

于 2013-06-28T18:53:34.697 回答