如何在MySQL中存储数组?

How to store arrays in MySQL?

提问人:tonga 提问时间:6/29/2013 最后编辑:self demandtonga 更新时间:4/7/2022 访问量:561414

问:

我在MySQL中有两个表。表 Person 包含以下列:

编号 名字 水果

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

fruit_name 颜色 价格
苹果 2
3
----------- -------- ------

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

mysql 数据库架构

评论

4赞 Janus Troelsen 6/29/2013
请参阅如何将数组存储到mysql中?和在MySQL中存储数组?和如何在MySQL中模拟数组变量?
1赞 Sai 6/29/2013
如何将其添加为单独的条目,橙色,2,1,玫瑰,2,1等,然后您可以使用查询将它们视为数组。
0赞 tonga 6/29/2013
@JanusTroelsen:我没有使用PHP来读/写数据库。那么有没有一种通用的方法可以做到这一点呢?
1赞 echo_Me 6/29/2013
@tonga检查我的小提琴,这是你想要的吗?

答:

-10赞 echo_Me 6/29/2013 #1

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

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

这里是小提琴的例子

评论

8赞 Martin F 3/14/2015
没有得到很好的解释。表名错误。
250赞 Bad Wolf 6/29/2013 #2

执行此操作的正确方法是在查询中使用多个表和它们。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_fruitpersonfruits

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

评论

13赞 tonga 6/29/2013
第三个表是 Person 和 Fruit 之间的链接表。所以如果一个人有 100 个水果。我需要在第三个表中创建 100 行,对吧?这有效率吗?
2赞 Bad Wolf 6/29/2013
@tonga 确切地说,这 100 行中的每一行都具有相同但不同的 .这实际上是Janus回答的理论的实现。person_idfruit_name
2赞 tonga 6/29/2013
两个表之间的任何关系都需要存储在第三个表中,这是否总是正确的?我可以通过仅存储两个表中的主键来执行查询以查找关系吗?
5赞 Bad Wolf 6/29/2013
是的,这就是现在示例的设置方式。关于这个人的任何信息都应该在表格中,关于表格中水果的任何信息,以及关于特定人与特定水果之间关系的任何信息都应该在表格中。因为在此示例中没有任何附加信息,所以该表只有两列,即 和 表的主键。然而,特定水果的数量是表格中其他东西的一个例子。personfruitsperson_fruitperson_fruitpersonfruitsperson_fruit
4赞 12431234123412341234123 8/21/2017
将 a 用于键并仅将其用于不是更好吗?因此,名称可以稍后更改,如果 in 中的行数不多,则所需的空间也更少。INTfruitsINTperson_fruitfruitsperson_fruit
88赞 Janus Troelsen 6/29/2013 #3

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

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

请阅读有关数据库规范化的信息。黄金法则是“[每个]非键[属性]必须提供有关键的事实,整个键,除了键之外什么都没有。数组执行的操作太多。它有多个事实,并存储顺序(与关系本身无关)。而且性能很差(见上文)。

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

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

评论

5赞 tonga 6/29/2013
谢谢Janus。这是有道理的。现在我明白了为什么MySQL不支持列中的数组类型。
3赞 tonga 6/29/2013
@Sai - 对于我正在做的事情,我真的需要NoSQL解决方案吗?
1赞 tonga 6/29/2013
好的,如果我有一个表,其中的字段包含数千个元素的数字数组,例如,从传感器收集的一些 2D 数据,那么使用 NoSQL DB 会更好吗?
7赞 Janus Troelsen 6/29/2013
@tonga:数据量并不能决定要使用的数据库类型,数据的性质决定了。如果没有关系,则不需要关系数据库。但是,由于这是行业标准,您可以保留它,只是不使用关系功能。大多数数据在某种程度上都是关系的!非规范化关系数据库或使用键值存储的一个常见原因是性能原因。但是,只有当您拥有数百万行时,才会出现这些问题!不要过早优化!我建议只使用 SQL 数据库(我推荐 PostgreSQL)。如果您有问题,请询问。
3赞 Janus Troelsen 6/29/2013
PostgreSQL 还内置了键值存储,这意味着如果它不适合你,那么离开关系模型会更容易。
71赞 Charles 6/29/2013 #4

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

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

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

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

69赞 Eric Grotke 9/5/2015 #5

需要考虑的一点是,您可以将数组存储在 Postgres 中。

评论

11赞 timetofly 8/6/2018
附加说明:它们可以被索引,因此查询检查数组中是否存在特定值可以非常快。复杂的 JSON 类型也是如此。
11赞 jhpratt 7/30/2019
这并不能以任何方式回答这个问题。OP询问了MySQL。
2赞 lumos42 8/20/2019
如果您在 Postgres 中使用 ArrayField,并且该列中有一个详尽的值列表(如固定的标签列表),您可以创建一个 GIN 索引 - 它将大大加快对该列的查询速度。
0赞 webdevfreak 7/6/2017 #6

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

编号: http://us.php.net/manual/en/function.serialize.php

返回值

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

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

评论

0赞 gouessej 7/3/2023
就性能而言,使用 blob 存储数组甚至比使用 JSON_ARRAYAGG 更糟糕。
53赞 drew 3/4/2018 #7

在 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,而不是从字符串中将数组组合在一起。

评论

0赞 gouessej 7/3/2023
请记住,就性能而言,使用 JSON_ARRAYAGG 并非没有成本,本机 SQL 数组支持将消除编码和解码。