跳到主要内容

数组

PostgreSQL 支持任意内置或用户定义类型的数组,例如 integer[]、text[] 和 uuid[]。数组适合元素数量有限、整体一起读写的值集合;如果元素需要独立查询、去重、排序或建立外键,通常应使用子表。

声明和写入​

CREATE TABLE articles (
article_id bigint PRIMARY KEY,
tags text[] NOT NULL DEFAULT '{}',
related_ids uuid[]
);

INSERT INTO articles (article_id, tags)
VALUES (1, ARRAY['sql', 'postgresql']);

数组字面量也可以写成字符串形式,但需要正确处理引号和反斜杠:

SELECT '{"sql","data types"}'::text[];

应用驱动通常能直接绑定数组,优先使用参数化接口。

下标、切片和维度​

PostgreSQL 数组下标默认从 1 开始:

SELECT tags[1], tags[1:2], array_length(tags, 1)
FROM articles;

数组可以多维,但每一维的边界和长度需要明确。空数组、NULL 数组和包含 NULL 元素的数组是不同状态:

SELECT cardinality('{}'::int[]), cardinality(ARRAY[NULL::int]);

不要假设所有数组都从 1 开始;手工构造的数组可以有其他下界,应使用 array_lower 和 array_upper 查询边界。

查询和展开​

SELECT article_id
FROM articles
WHERE 'sql' = ANY(tags);

SELECT a.article_id, tag
FROM articles AS a
CROSS JOIN LATERAL unnest(a.tags) AS tag;

ANY、ALL、@>、<@ 和 && 分别支持元素匹配、包含和重叠判断。unnest 把数组变成行,适合统计和连接,但大量展开会增加查询成本。

数组索引​

CREATE INDEX articles_tags_gin_idx
ON articles USING gin (tags);

GIN 适合数组包含和重叠查询。表达式索引可支持固定下标或数组长度过滤,但查询表达式必须匹配索引定义。索引不会自动让任意 unnest 查询变快。

修改与并发​

数组元素更新会产生新的行版本:

UPDATE articles
SET tags = array_append(tags, 'index')
WHERE article_id = $1;

多个会话同时修改同一数组时仍会产生行级更新冲突。若标签需要独立审计、频繁增删或按标签反向查询,使用关联表通常更可靠:

CREATE TABLE article_tags (
article_id bigint REFERENCES articles,
tag text NOT NULL,
PRIMARY KEY (article_id, tag)
);

常见误区​

  • 忘记数组下标默认从 1 开始。
  • 把空数组、NULL 数组和 [NULL] 当成同一状态。
  • 用数组保存需要外键、唯一约束或独立权限的实体。
  • 用字符串逗号分隔代替数组,之后无法可靠处理元素中的逗号和转义。
  • 建立 GIN 后不检查查询计划和数据选择性。