当前位置:首页 > 技术知识 > 正文内容

第 16 章:数据类型进阶 - PostgreSQL入门

maynowei7个月前 (08-07)技术知识72

在第 4 章,我们已经学习了像 INT, VARCHAR, DATE 这样的基本数据类型。它们就像是工具箱里的螺丝刀和扳手,能解决大部分日常问题。


但是,PostgreSQL 之所以强大,很大程度上因为它提供了一个极其丰富的“军火库”,里面有各种为特定场景量身打造的高级数据类型。用好它们,能极大地提升数据存储的效率、安全性和查询的便利性。

这一章,我们就来认识几位重量级选手:

  • SERIAL: 实现自动增长的整数 ID。
  • UUID: 全局唯一标识符,应对分布式系统的挑战。
  • ARRAY: 在一个字段里存储多个值。
  • JSON/JSONB: 在关系型数据库中优雅地处理半结构化数据。
  • TIMESTAMPTZ: 带时区的时间戳,解决全球化应用的时间问题。

16.1SERIAL和BIGSERIAL:自增主键

在之前的例子中,我们为 id 列手动插入 1, 2, 3... 这样的值。这很麻烦,而且在多用户同时插入时,很容易产生冲突。

SERIAL 类型就是为了解决这个问题而生的。它并不是一个真正的数据类型,而是一个“语法糖”,一个快捷方式。

当你将一列定义为 SERIAL 时,PostgreSQL 会自动为你做三件事:

  1. 创建一个序列 (Sequence) 对象,这是一个专门用来生成不重复数字的计数器。
  2. 将列的数据类型设置为 INTEGER
  3. 将列的默认值设置为从上面那个序列中获取下一个值。

使用方法:

CREATE TABLE products (
    product_id SERIAL PRIMARY KEY, -- 看这里!
    product_name VARCHAR(100) NOT NULL
);

现在,当我们插入数据时,完全不需要product_id 提供值,数据库会自动为我们生成!

INSERT INTO products (product_name) VALUES ('笔记本电脑');
INSERT INTO products (product_name) VALUES ('机械键盘');

SELECT * FROM products;

执行结果:

 product_id | product_name
------------+--------------
          1 | 笔记本电脑
          2 | 机械键盘
(2 rows)

product_id 被自动、安全地赋予了 1 和 2。

  • SERIAL: 对应 INTEGER,最大值约 21 亿。对于绝大多数应用都够用了。
  • BIGSERIAL: 对应 BIGINT,最大值约 9 百京(9 x 10^18)。如果你的表可能会有天文数字级别的行数(比如日志、事件记录),请使用 BIGSERIAL

16.2UUID数据类型

SERIAL 生成的 ID 在单台数据库内是唯一的,但如果你的系统是分布式的,有多台数据库都需要生成 ID,SERIAL 就可能产生冲突。

UUID (Universally Unique Identifier) 就是为解决这个问题而生的。它是一个 128 位的数字,通常表示为 32 个十六进制数的字符串,比如
550e8400-e29b-41d4-a716-446655440000

UUID 的值是通过算法生成的,它能保证在全世界的范围内,你生成的每一个 UUID 都是几乎不可能重复的。

使用方法:
首先,我们需要启用一个内置的扩展
uuid-ossp,它提供了生成 UUID 的函数。

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

然后,在建表时使用 UUID 类型,并设置默认值为 uuid_generate_v4() (生成版本 4 的随机 UUID)。

CREATE TABLE documents (
    doc_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    title TEXT
);

INSERT INTO documents (title) VALUES ('第一份机密文件');
INSERT INTO documents (title) VALUES ('第二份机密文件');

SELECT * FROM documents;

执行结果:

               doc_id               |      title
------------------------------------+--------------------
 a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d | 第一份机密文件
 b6c7d8e9-f0a1-4b2c-8d9e-1f2a3b4c5d6e | 第二份机密文件
(2 rows)

doc_id 被赋予了全局唯一的、无规律的 ID。


16.3ARRAY数组类型

有时候,我们需要在一个字段里存储一组同类型的值,比如一篇文章的多个标签、一个用户的多个电话号码。

传统做法是再建一张关联表(比如 post_tags 表),但这对于简单的场景来说有点“杀鸡用牛刀”。PostgreSQL 的 ARRAY 类型提供了一个更直接的解决方案。

你可以创建任何基础数据类型的数组,比如 INTEGER[], TEXT[], DATE[]

使用方法:

CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title TEXT,
    tags TEXT[] -- 文本类型的数组
);

INSERT INTO articles (title, tags)
VALUES ('学习SQL', '{"sql", "database", "postgres"}');

INSERT INTO articles (title, tags)
VALUES ('学习Python', ARRAY['python', 'programming']); -- 另一种语法

SELECT * FROM articles;
  • 数组的值可以用 {} 大括号括起来的字符串表示,也可以用 ARRAY[...] 语法。

查询数组:
PostgreSQL 提供了一系列强大的函数和操作符来查询数组。

-- 找出所有包含 'sql' 标签的文章
SELECT title FROM articles WHERE 'sql' = ANY(tags);

-- 找出所有同时包含 'python' 和 'programming' 标签的文章
SELECT title FROM articles WHERE tags @> ARRAY['python', 'programming'];

16.4JSON和JSONB:处理半结构化数据

这可能是 PostgreSQL 最强大的特性之一。它允许你在关系型数据库中,原生、高效地存储和查询 JSON 数据。

  • JSON: 以纯文本形式存储 JSON 数据。它会检查 JSON 格式是否合法,但仅此而已。
  • JSONB: 以二进制格式存储 JSON 数据。它在存入时会做一些预处理,比如去掉重复的键、排序键等。

JSON vs JSONB:

特性

JSON

JSONB

存储

纯文本,保留原始格式(空格、顺序、重复键)

二进制,不保留格式,效率更高

写入速度

(因为只做格式检查)

稍慢 (因为要解析和转换)

查询速度

(每次查询都要重新解析)

极快 (因为可以被索引)

索引支持

有限

非常强大 (支持 GIN 索引)

黄金法则 :除非你有特殊需求要保留 JSON 的原始文本格式,否则永远优先使用 JSONB

使用方法:

CREATE TABLE user_profiles (
    user_id INT PRIMARY KEY,
    profile JSONB
);

INSERT INTO user_profiles (user_id, profile) VALUES
(1, '{
    "name": "张三",
    "contact": {
        "email": "zhangsan@example.com",
        "phones": ["138...", "139..."]
    },
    "interests": ["reading", "coding"]
}');

-- 查询 JSON 内部的字段
-- -> 操作符返回一个 JSON 对象,->> 操作符返回文本
SELECT
    profile -> 'name' AS json_name, -- 返回 "张三" (带引号的JSON字符串)
    profile ->> 'name' AS text_name, -- 返回 张三 (纯文本)
    profile -> 'contact' ->> 'email' AS email -- 链式查询
FROM user_profiles
WHERE user_id = 1;

-- 查询 JSON 数组中的元素
SELECT profile -> 'interests' -> 0 FROM user_profiles; -- 返回 "reading"

-- 检查是否存在某个键
SELECT * FROM user_profiles WHERE profile ? 'name';

16.5TIMESTAMPvsTIMESTAMPTZ(带时区)

  • TIMESTAMP: TIMESTAMP WITHOUT TIME ZONE 的别名。它只记录日期和时间,比如 2023-10-27 10:00:00。它不包含任何时区信息。
  • TIMESTAMPTZ: TIMESTAMP WITH TIME ZONE 的别名。这是 PostgreSQL 中处理时间最推荐的方式。

TIMESTAMPTZ 的工作方式非常聪明:

  1. 当你插入一个时间值时,它会根据你当前数据库会话的时区设置,将这个时间转换成 UTC (协调世界时) 进行存储。
  2. 当你查询这个值时,它又会根据你当前会话的时区设置,将存储的 UTC 时间转换回你本地的时区时间来显示。

这意味着,无论你的用户和服务器在世界哪个角落,TIMESTAMPTZ 存储的都是一个绝对的、无歧义的时间点

黄金法则 :只要你的应用需要处理不同时区的用户,或者服务器可能部署在不同时区,请始终使用 TIMESTAMPTZ


本章小结

你已经为你的数据表装备上了一批精良的“特种武器”!

  • 我们学会了用 SERIAL 来创建自增主键
  • UUID 来应对分布式系统的唯一 ID 需求。
  • ARRAY 来方便地存储数组
  • 用强大的 JSONB 来拥抱半结构化数据
  • TIMESTAMPTZ 来一劳永逸地解决时区问题。

为你的数据选择最合适的数据类型,是数据库设计的核心一环。一个好的设计,能让你的应用事半功倍。

从下一章开始,我们将进入第四部分:数据完整性与表设计。我们将学习如何通过各种“约束”来保证数据的质量和一致性,这是构建一个健壮、可靠的数据库系统的基石。我们下一章见!

相关文章

Windows 加密盘BitLocker爆锁屏绕过严重漏洞

BitLocker Windows内置现代设备级数据加密保护功能,BitLocker与Windows内核深度集成。有大量的企业和个人使用BitLocker加密自己关键数据,以防止数据泄密。BitLoc...

Shopee新手指南:Shopee卖家中心用户界面介绍

1.Shopee各站点前台网页链接:2.Shopee各站点后台网页链接3.Shopee APP下载:安卓版下载链接:https://pan.baidu.com/s/1eSp8M1k#list/path...

从 async/await 到虚拟线程:Python 并发的再思考

演进之路:从async/await到线程的反思首先必须明确的是,async/await对Python并非全无裨益:它最大的价值,是让更多人接触到了并发编程。通过在编程语言中嵌入语法元素,并发编程的门槛...

Navicat Premium 连接 Oracle 数据库

Navicat Premium是一个可多重连接的数据库管理工具,它可让你以单一程序同時连接到 MySQL、SQLite、Oracle 及 PostgreSQL 数据库,让管理不同类型的数据库更加方便。...

Oracle数据库无法连接问题排查(oracle数据库连接不成功)

数据库告警日志 如下图 。发现 问题时间段,没有 数据库服务故障 报错,但是存在较多 TNS-12535 、 12560 、 12170 、 00505 错误:通过检查问题时间段应用日志, 也记录了...

Oracle工具PLSQL登录时提示初始化失败,无法锁定OCI.dll错误解决

报错信息: (没有登录)-PL/SQL Developer Initialization error 无法锁定OCI dll OracleHomeKey: OracleHomeDir:E\instan...