编程全栈开发者

如何在SQL中使用数组和类似数组的结构来存储和分析单元格中的多个值,以及何时这种方法是合理的?

用 Hintsage AI 助手通过面试

答案。

问题背景

经典SQL不支持在单元格中存储多个值——关系模型要求进行规范化。然而,在现代任务中,常常会遇到"标签列表"、"评分尺度"等字段,其中在单独行的级别上操作多个值是很方便的。一些数据库(PostgreSQL、Oracle)提供了ARRAY数据类型或类似机制。

问题

使用数组违反了规范化原则,给许多操作(过滤、更新、索引)带来了困难,并使得代码在数据库之间的移植性降低。但在某些情况下,这可能是方便或不可避免的——例如,用于缓存或快速查找小列表中的值。

解决方案

  • 在PostgreSQL中原生支持数组。示例:
CREATE TABLE products ( id SERIAL PRIMARY KEY, tags TEXT[] ); -- 插入: INSERT INTO products(tags) VALUES (ARRAY['eco','sale','hot']); -- 按数组查找: SELECT * FROM products WHERE 'eco' = ANY (tags);
  • 在MySQL 5.x中没有数组,通常使用JSON或分隔字符串及解析函数。
  • 在Oracle中——集合,嵌套表/可变数组。
  • 对于最佳的分析任务,最好进行规范化(创建连接的二级表product_tags)并使用JOIN,而仅在特殊情况下存储数组(性能或特定要求)。

关键特性:

  • 当数组确实需要且数据库支持时很方便。
  • 当数组很大时,索引和过滤会有问题。
  • 在数据库之间不可移植,增加了维护难度。

具有挑战性的问题。

可以对数组中的单个元素建立索引吗?

在PostgreSQL中——可以,使用GIN/GIST索引:

CREATE INDEX idx_tags ON products USING GIN (tags);

如何更快地检查值是否包含在通过分隔符划分的字符串列中的数组中?

SQL标准不支持,使用模式匹配来查找:

SELECT * FROM users WHERE ',admin,' LIKE concat('%,',role,',%');

但这种方法不可靠且较慢。

可以在数组中存储多少个值,以及有什么限制?

限制取决于数据库——例如,在PostgreSQL中,限制仅在行大小(1-2 MB)上。

常见错误和反模式

  • 为了"简单"而在一个单元格中存储数组,从而使分析复杂化
  • 通过LIKE错误地过滤值而不考虑分隔符
  • 依赖于字符串数组的唯一性和索引

实际案例

消极案例

在一个电子商务项目中,决定通过逗号在一个列中将产品标签存储为字符串。快速按标签查找产品变得非常困难,过滤错误频繁发生,由于解析错误,标签重复。

优点:

  • "简单"且快速实现

缺点:

  • 在规模扩大时非常慢,维护复杂,无法保证值的唯一性

积极案例

在PostgreSQL中,对于小型的不变集合(用户角色),使用了ARRAY和GIN索引。对于较大的集合——单独的角色表。

优点:

  • 通过索引在ARRAY中快速查找
  • 在需要的地方保持与关系模型的兼容性

缺点:

  • 不可移植,需要了解数据库的扩展特性