当前位置:首页 > 问答 > 正文

数据库里头怎么放数组?几种存储方法简单聊聊,方便理解和操作

说到在数据库里放数组,这确实是个很实际的问题,因为我们在编程语言里用数组或者列表太方便了,但一到要存进关系型数据库(比如常用的MySQL、PostgreSQL),就发现它那张横平竖直的表格,好像不太擅长处理这种“一个字段里有多个值”的情况,别急,方法总比困难多,我们来聊聊几种常见的处理方式。

第一种方法:老老实实不用数组,用关系表(最经典的方法)

这是关系型数据库设计中最正统、最规范的做法,核心思想就是:既然数据库表格是二维的,那我就把数组“摊平”,用多行数据来表示。

举个例子,比如我们要存储用户和他们拥有的多个电话号码,如果用一个数组字段 phone_numbers 来存,会违反数据库设计范式,带来很多麻烦,那该怎么做呢?

我们设计两张表:

  1. 用户表 (users):存放用户的核心信息,比如用户ID、用户名。
    • user_id (主键)
    • username
  2. 电话号码表 (user_phones):专门存放电话号码,每个电话号码占一行,并通过“用户ID”这个字段关联回用户表。
    • phone_id (主键)
    • user_id (外键,关联到users表的user_id)
    • phone_number

这样一来,一个用户有3个电话号码,就在 user_phones 表里有3条对应的记录,这完美地模拟了一个动态的“电话号码数组”。

  • 好处在哪里?

    • 查询灵活:你可以轻松地查询“谁拥有某个特定号码”,或者“找出所有拥有超过2个号码的用户”。
    • 维护方便:增删改一个号码,就是增删改一条记录,非常清晰。
    • 符合规范:数据库结构清晰,是经过长期实践检验的最佳方式。
  • 麻烦在哪里?

    • 稍微复杂:每次查询都需要连接(JOIN)两张表,对于简单场景来说,感觉有点“杀鸡用牛刀”。
    • 需要理解关系概念:需要对主键、外键有基本了解。

这种方法在大多数情况下都是首选,尤其是在数据关系复杂、需要频繁进行复杂查询的场景下。

第二种方法:用逗号隔开,拼成一个字符串(简单粗暴的方法)

这是非常常见的一种“偷懒”做法,就是把数组里的所有元素,用某个分隔符(最常见的是逗号)连接起来,塞进一个普通的文本字段(VARCHAR)里。

继续上面的例子,在 users 表里,我们直接增加一个 phone_numbers 字段,然后把用户的号码写成 "13800138000,13900139000,15800158000" 这样的字符串存进去。

  • 好处在哪里?

    • 极其简单:不需要设计额外的表,结构一目了然。
    • 读取方便:一次就能把整个“数组”读出来,在程序里用 split(',') 这样的方法就能还原成数组。
  • 麻烦在哪里?(这个非常关键!)

    • 查询是噩梦:如果你想查询“谁拥有13800138000这个号码”,你需要写非常别扭的SQL,WHERE phone_numbers LIKE '%13800138000%',这会导致全表扫描,效率极低,而且可能出错(比如号码13800138000会被包含13800138001的字段匹配到)。
    • 更新麻烦:要删除或修改其中的一个号码,你必须先把整个字符串读出来,在程序里处理完,再整个写回去,容易出错且不是原子操作。
    • 无法保证数据完整性:数据库无法验证每个被逗号隔开的部分是否是一个有效的电话号码。

这种方法一般只适用于那些你永远不需要在数据库层面根据数组内的元素进行查询、筛选、排序的场景,仅仅是为了存储和整体读取。通常不推荐使用。

第三种方法:使用数据库自带的数组类型(高级且方便的方法)

一些现代的数据库系统,PostgreSQL,直接提供了原生的数组数据类型,这意味着你可以定义一个字段的类型就是 TEXT[](文本数组)、INTEGER[](整数数组)等。

在PostgreSQL里,你可以这样操作:

  • 建表:CREATE TABLE users (user_id SERIAL, username TEXT, phone_numbers TEXT[]);

  • 插入数据:INSERT INTO users VALUES (1, '张三', '{"13800138000", "13900139000"}');

  • 查询包含某个号码的用户:SELECT * FROM users WHERE '13800138000' = ANY(phone_numbers);

  • 好处在哪里?

    • 真正的数组:数据库引擎层面支持,查询、索引都可以针对数组元素进行,功能强大。
    • 操作方便:有专门的运算符和函数来处理数组,比如追加元素、获取长度等。
  • 麻烦在哪里?

    • 数据库支持:不是所有数据库都支持(比如MySQL的主流版本就不支持)。
    • 有一定学习成本:需要学习特定数据库的数组操作语法。
    • 迁移性差:如果你想把数据库从PostgreSQL换到MySQL,这个字段的处理会是个大问题。

如果你的项目确定使用PostgreSQL这类支持数组的数据库,并且确实有在数据库里直接操作数组元素的需求,这是一个非常好的选择。

第四种方法:用JSON格式存储(灵活通用的方法)

现在很多数据库(MySQL 5.7+, PostgreSQL, MongoDB等)都加强了对JSON数据类型的支持,你可以把一个数组(甚至是更复杂的对象)序列化成JSON字符串,存到专门的JSON或TEXT字段中。

在MySQL中:

  • 建表:CREATE TABLE users (user_id INT, username VARCHAR(100), phone_numbers JSON);

  • 插入数据:INSERT INTO users VALUES (1, '张三', '["13800138000", "13900139000"]');

  • 查询:SELECT * FROM users WHERE JSON_CONTAINS(phone_numbers, '"13800138000"');

  • 好处在哪里?

    • 极其灵活:不仅能存数组,还能存任意复杂的嵌套结构。
    • 应用广泛:JSON是前后端通吃的标准数据格式,序列化和反序列化非常方便。
    • 一定的查询能力:现代数据库提供了JSON路径查询等功能,查询能力比单纯的逗号分隔字符串强得多。
  • 麻烦在哪里?

    • 查询性能:虽然能查,但性能通常不如原生的关系表结构,尤其是数据量巨大、查询复杂时。
    • 结构约束弱:数据库难以对JSON内部的字段结构进行严格的约束和验证。

JSON方式非常适合存储一些不需要频繁深入查询、结构可能变化频繁的配置项、附加属性等。

简单总结一下:

  • 追求规范、复杂查询、数据完整性:首选第一种方法(关系表)
  • 只是简单存储,绝不单独查询数组内容:可以考虑第二种方法(逗号分隔),但要清楚其弊端。
  • 数据库支持且需要真数组操作:选用第三种方法(原生数组类型),如PostgreSQL。
  • 存储灵活、可变的结构化数据第四种方法(JSON) 是现代应用中的热门选择。

没有绝对最好的方法,只有最适合你当前项目需求和技术栈的选择,希望这些简单的解释能帮你理清思路。

数据库里头怎么放数组?几种存储方法简单聊聊,方便理解和操作