如何在PostgreSQL中创建和使用自定义数据类型?

来源:个人站长网作者:松松建站头衔:草根站长
导读:本期聚焦于松松建站创作的《如何在PostgreSQL中创建和使用自定义数据类型?》,敬请观看详情。PostgreSQL允许用户通过CREATE TYPE命令扩展类型系统,而不是只能依赖内置的整数、文本和日期类型。自定义数据类型能把多个相关字段打包成一个复合结构,也能定义枚举集合限制取值,还可以基于现有类型派生新的域。创建复合类型后,表字段可以直接使用该类型,查询时通过字段访问语法读取内部数据,函数也能接收或返回这种类型,显著增强数据模型的表达力。枚举类型适合状态字段,可以防止非法值写入,同时保留可读性。修改自定义类型前需要处理依赖关系,删除类型则可使用DROP TYPE命令,并配合CASCADE级联清理。掌握这些机制后,在业务表设计中能以更贴近领域语义的方式组织数据,减少冗余并提升约束力。本文结合示例说明复合类型、枚举类型和域类型的创建、使用与维护要点。

PostgreSQL 的类型系统并不局限于整数、字符和日期这几类内置标量。数据库允许开发人员通过 CREATE TYPE 命令定义自己的数据类型,这种能力在构建更贴近业务语义的表结构时非常关键。自定义类型可以减少冗余字段、强化取值约束,并让函数签名更清晰。接下来从类型分类、创建语法、实际应用和后期维护几个角度展开说明。

如何在PostgreSQL中创建和使用自定义数据类型?

一、PostgreSQL 支持哪些自定义数据类型

PostgreSQL 的自定义数据类型大致可以分为复合类型、枚举类型、域类型、范围类型和基础类型。复合类型用于把多个字段打包成一个独立结构,枚举类型用于限制某列只能取预定义值,域类型是在已有类型上附加约束而不创建新的底层表示,范围类型则可以表达区间数据。对于大多数日常业务场景,复合类型和枚举类型使用最多,域类型也能有效提升数据质量。

复合类型的典型例子是地址。一个收货地址通常包含街道、城市、邮政编码等多个字段,如果这些字段分散在每一张需要地址的表中,会出现大量重复定义。将地址定义成一个复合类型后,任意表只需要一个 address 字段即可保存全部信息。查询时可以通过括号语法访问内部属性,例如 (address).city。这种设计让数据结构更接近领域模型,也便于统一维护。

枚举类型适合状态、级别等取值固定的字段。以前开发者常使用整型或文本表示状态,例如用0、1、2表示草稿、发布、归档,但整型无法阻止写入99这样的非法值,文本又容易因为大小写或拼写不一致产生脏数据。枚举类型可以从数据库层面约束取值范围,同时保留可读性。PostgreSQL 会把枚举值按声明顺序排序,因此还可以直接使用比较运算符。

二、使用 CREATE TYPE 创建复合类型与枚举类型

复合类型通过 CREATE TYPE ... AS 语法创建。创建后,类型名称可以像内置类型一样用于表字段、函数参数或函数返回值。下面先定义一个 address_type 复合类型,它包含 street、city 和 postal_code 三个属性。

CREATE TYPE address_type AS (
    street text,
    city text,
    postal_code varchar(20)
);

这段代码执行后,可以在建表时直接引用 address_type。插入数据时需要把整个复合值写成 ROW 表达式,或者用字符串字面量转换。查询内部字段时使用括号语法,因为类型名和字段名之间存在优先级问题,不写括号会被解析成表名或列名。下面代码展示建表、插入和查询的完整流程。

CREATE TABLE customer_address (
    id serial PRIMARY KEY,
    customer_id integer NOT NULL,
    address address_type
);

INSERT INTO customer_address (customer_id, address)
VALUES (1, ROW('科技路88号', '西安', '710000'));

SELECT id,
       (address).street AS street,
       (address).city AS city
FROM customer_address
WHERE (address).city = '西安';

注意 WHERE 条件中同样需要用括号包裹 address,否则 PostgreSQL 可能将 city 解析为当前查询中的独立列,导致错误。复合类型也支持嵌套,例如可以在 address_type 中放入另一个复合类型,但嵌套过深会让 SQL 语句变得难以阅读,需要权衡。

枚举类型的创建使用 CREATE TYPE ... AS ENUM 语法。枚举值以单引号逐个列出,顺序就是排序顺序。例如创建一个文章状态枚举类型,并把它用于博客表。

CREATE TYPE article_status AS ENUM ('draft', 'published', 'archived');

CREATE TABLE article (
    id serial PRIMARY KEY,
    title text NOT NULL,
    status article_status NOT NULL DEFAULT 'draft'
);

INSERT INTO article (title, status)
VALUES ('PostgreSQL 自定义类型', 'published');

如果尝试插入枚举值之外的文本,例如 'deleted',PostgreSQL 会抛出错误,提醒该值不是 article_status 类型的合法成员。这种约束比使用简单的 CHECK 约束更直观,也不容易出现重复定义不一致的问题。枚举值之间可以比较大小,例如 'draft' < 'published' 在布尔判断中成立,因为 draft 在声明时位于 published 之前。

三、域类型与范围类型的使用

域类型使用 CREATE DOMAIN 创建,它不生成新的底层存储结构,而是给已有类型增加约束和默认值。比如要确保所有邮箱字段格式正确,可以定义一个 email_domain 域,只在创建时维护一次正则校验即可。

CREATE DOMAIN email_domain AS text
CHECK (VALUE ~ '^[^@]+@[^@]+$');

CREATE TABLE user_account (
    id serial PRIMARY KEY,
    email email_domain NOT NULL
);

域类型非常适合统一业务约束。如果多个表都需要保存手机号、邮政编码、百分比等字段,可以通过域类型保证规则一致。当约束发生变化时,只需修改域定义,而无需逐表调整。但域类型本质上是文本或数值,查询时并不区分来源,因此它更多用于数据完整性而非语义建模。

PostgreSQL 还支持范围类型,可以表达如会议时间、价格区间等连续或离散范围。虽然系统已经提供了 int4range、numrange、tsrange 等内置范围类型,但你也可以基于自己的基础类型创建范围类型。下面的例子基于 numeric 创建了一个价格范围类型。

CREATE TYPE price_range AS RANGE (
    subtype = numeric,
    subtype_diff = numeric_sub_diff
);

范围类型可以自动处理包含、重叠、相邻等关系,例如使用 && 运算符判断两个范围是否重叠,使用 @> 判断一个范围是否包含另一个范围。范围类型在某些排期系统或价格模型中非常有用,但复杂度较高,如果不是明确的区间建模需求,不必优先使用。

四、修改与删除自定义数据类型

自定义类型创建后并非一成不变。复合类型可以通过 ALTER TYPE 增加或删除属性,枚举类型可以增加新的枚举值。例如给 address_type 增加一个 country 字段,可以用以下命令。

ALTER TYPE address_type ADD ATTRIBUTE country text;

枚举类型增加值的语法是 ALTER TYPE article_status ADD VALUE 'deleted'。需要注意的是,在 PostgreSQL 的某些版本中,新增枚举值不能放在同一个事务中立即使用,需要先提交事务再读取,否则可能出现错误。删除枚举值则长期没有直接支持,只能通过重建类型的间接方式完成,因此在设计枚举值时尽量预留一定的扩展空间。

删除自定义类型使用 DROP TYPE 命令。如果类型已经被表、函数或视图引用,直接删除会失败。这时可以指定 CASCADE 选项,让数据库自动删除依赖该类型的对象,但操作前必须确认影响范围,避免误删重要数据。

DROP TYPE address_type CASCADE;

依赖管理是自定义类型维护中的核心风险。例如一个复合类型被多张表引用,修改其属性可能导致查询列名变化,进而影响应用程序的 SQL 语句。如果业务模型经常变化,自定义复合类型会降低迁移灵活性,此时可以考虑改用单独的关联表或 JSONB 列。但对于结构稳定、语义明确的字段集合,自定义类型仍然是比分散字段更优的选择。

总结来说,PostgreSQL 自定义数据类型是一把强大的建模工具。复合类型适合封装固定结构,枚举类型适合约束有限取值,域类型适合统一约束规则,范围类型适合表达区间。使用时要结合业务稳定性、查询复杂度和维护成本来权衡,避免为了抽象而引入不必要的复杂度。

PostgreSQL自定义数据类型CREATE TYPE复合类型修改时间:2026-08-26 23:24:10

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。