组合类型
组合类型(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 是不同状态,查询时不要只判断整个值是否为空。
常见误区
- 以为组合类型自动提供字段级非空和格式约束。
- 访问字段时漏写括号,导致解析为表别名或函数调用。
- 把需要独立查询和更新的实体塞进一个组合列。
- 忽略表行类型变化对函数、视图和客户端映射的影响。
- 依赖驱动自动解析组合文本,未做版本和边界测试。