跳到主要内容

组合类型

组合类型(composite type)是一组带名称和类型的字段,类似一个轻量结构体。PostgreSQL 会为每张表自动创建对应的行类型,也允许使用 CREATE TYPE ... AS (...) 定义独立组合类型。

定义和使用​

CREATE TYPE address AS (
city text,
postal_code text,
street text
);

CREATE TABLE customers (
customer_id bigint PRIMARY KEY,
shipping_address address
);

INSERT INTO customers VALUES
(1, ROW('杭州', '310000', '文三路')::address);

访问嵌套字段时要注意括号:

SELECT (shipping_address).city
FROM customers;

ROW(...) 可以构造组合值;客户端驱动对组合值的解析支持程度不同,API 边界通常更适合返回 JSON 或展开为普通列。

组合类型与表行类型​

CREATE TABLE products (
product_id bigint PRIMARY KEY,
name text NOT NULL,
price numeric(12, 2) NOT NULL
);

SELECT p
FROM products AS p;

这里的 p 是 products 的行类型。函数可以接收或返回整行:

CREATE FUNCTION product_name(products)
RETURNS text
LANGUAGE sql
AS $$ SELECT $1.name $$;

表结构变化会影响行类型和依赖它的函数、视图;迁移前应检查依赖关系。

约束边界​

独立组合类型本身不能像表那样直接附加 NOT NULL、CHECK 或外键约束。约束通常放在包含它的表列上,或通过域、触发器、拆分普通列实现:

ALTER TABLE customers
ADD CONSTRAINT customer_city_check
CHECK ((shipping_address).city IS NULL OR length((shipping_address).city) > 0);

如果字段需要独立索引、唯一性、审计或部分更新,使用普通列或关联表往往比组合列更合适。

数组、函数和查询​

组合类型可以组成数组,也可以在函数中作为输入输出。展开组合数组时可使用 unnest 并显式指定字段:

SELECT (item).city
FROM unnest(ARRAY[
ROW('杭州', '310000', '文三路')::address,
ROW('北京', '100000', '东直门')::address
]) AS item;

组合字段为 NULL、组合值本身为 NULL、字段值为 NULL 是不同状态,查询时不要只判断整个值是否为空。

常见误区​

  • 以为组合类型自动提供字段级非空和格式约束。
  • 访问字段时漏写括号,导致解析为表别名或函数调用。
  • 把需要独立查询和更新的实体塞进一个组合列。
  • 忽略表行类型变化对函数、视图和客户端映射的影响。
  • 依赖驱动自动解析组合文本,未做版本和边界测试。