运动健康计步器手机版
30.07MB · 2025-09-22
如果你接触过数据库,一定听过“关系型数据库”(比如MySQL、Oracle)——它们用表、行、列存储数据,用SQL语言操作。而PostgreSQL不一样,它是对象-关系型数据库(ORDBMS):既保留了关系型数据库的“规矩”(比如表结构、SQL标准),又融入了对象型数据库的“灵活”(比如自定义数据类型、方法)。
举个通俗的例子:如果要存储“几何图形”数据,传统关系型数据库可能会用多个字段(比如x1,y1,x2,y2
存矩形),但PostgreSQL允许你自定义一个rectangle
类型,直接存储矩形的坐标,还能给这个类型加个area()
方法(计算面积)。这就是“对象”特性的体现——把“数据”和“操作数据的方法”打包在一起;而“关系”特性则是它依然遵守SQL规则,能用表、JOIN、外键这些传统玩法。
PostgreSQL不是“凭空造出来的”,它的祖先叫POSTGRES,是1986年美国加州大学伯克利分校(UC Berkeley)的科研项目。当时的关系型数据库有个大痛点:无法处理复杂数据类型(比如几何图形、文档)。POSTGRES的目标就是解决这个问题——它首次提出“对象-关系”模型,允许用户自定义数据类型和函数。
1996年,POSTGRES的开源版本发布,改名为“PostgreSQL”(意为“PostgreSQL是POSTGRES的后继者”)。从那以后,全球开发者一起维护它,现在已经更新到17版本(2024年),成为最流行的开源数据库之一。
PostgreSQL能火,不是因为“开源免费”,而是因为它的“硬实力”——支持很多现代数据库的核心特性,而且做得很极致。我们逐一拆解:
PostgreSQL的查询能力堪称“全能”,支持:
orders
(订单表)、users
(用户表)、products
(商品表)JOIN起来;WITH
语句把复杂查询拆成“临时表”,比如查“每个部门的TOP3工资员工”,用CTE先算每个部门的工资排名,再取前3。举个电商系统的实际例子——热销商品统计:
-- 第一步:用CTE算出每个商品的总销量
WITH product_sales AS (
SELECT product_id, SUM(quantity) AS total_sold
FROM orders
GROUP BY product_id
)
-- 第二步:关联商品表,得到热销TOP10
SELECT p.name, ps.total_sold
FROM products p
JOIN product_sales ps ON p.id = ps.product_id
ORDER BY ps.total_sold DESC LIMIT 10;
这个查询用CTE拆解了复杂逻辑,PostgreSQL处理起来毫无压力。
你肯定遇到过“转账失败”的坑:从A账户扣了钱,B账户却没收到——这就是“事务不完整”。PostgreSQL支持ACID特性,彻底解决这个问题:
传统数据库里,“读”和“写”会互相阻塞——比如你在看数据时,别人不能改;别人在改数据时,你不能看。这在高并发场景下(比如秒杀、电商大促)会卡死。
PostgreSQL用**MVCC(多版本并发控制)**解决了这个问题:每个事务看到的数据是一个“快照”。比如:
这样一来,“读”和“写”可以同时进行,不会阻塞,大大提高了并发性能。
PostgreSQL还能帮你“自动做事”,不用手动检查数据:
orders
表的user_id
必须指向users
表的id
。如果有人想插入一个不存在的user_id
,PostgreSQL会直接拒绝,防止“无效订单”;-- 1. 创建触发器函数(要做的事情)
CREATE OR REPLACE FUNCTION update_inventory()
RETURNS TRIGGER AS $$
BEGIN
-- 减少商品库存(NEW代表新插入的订单记录)
UPDATE products
SET stock = stock - NEW.quantity
WHERE id = NEW.product_id;
RETURN NEW; -- 返回新记录,继续执行插入操作
END;
$$ LANGUAGE plpgsql; -- 用PL/pgSQL写函数(PostgreSQL内置语言)
-- 2. 绑定触发器到orders表的INSERT操作
CREATE TRIGGER order_insert_trigger
AFTER INSERT ON orders -- 当插入订单后执行
FOR EACH ROW -- 每插入一行都执行
EXECUTE FUNCTION update_inventory(); -- 执行上面的函数
当你插入一个订单时,这个触发器会自动更新商品库存——是不是很省心?
如果你觉得PostgreSQL的“默认功能”不够用,没关系——它允许你自定义几乎所有东西,堪称“数据库里的乐高”:
比如你要存储“电话号码”,可以定义一个phone_number
类型:
-- 1. 创建自定义类型(包含国家码、区号、号码)
CREATE TYPE phone_number AS (
country_code VARCHAR(3), -- 国家码(比如+86)
area_code VARCHAR(3), -- 区号(比如010)
number VARCHAR(8) -- 号码(比如12345678)
);
-- 2. 用这个类型建表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
phone phone_number -- 使用自定义类型
);
-- 3. 插入数据(用ROW()包裹自定义类型的值)
INSERT INTO users (name, phone)
VALUES ('张三', ROW('+86', '010', '12345678'));
-- 4. 查询数据(用.(字段名)取自定义类型的值)
SELECT name, (phone).country_code || '-' || (phone).area_code || '-' || (phone).number AS full_phone
FROM users;
PostgreSQL支持用多种语言写函数(比如PL/pgSQL、Python、Java)。比如你要计算两个点之间的距离,可以写一个函数:
-- 1. 定义点类型(PostgreSQL内置了geometry类型,但这里用自定义类型举例)
CREATE TYPE point AS (x INT, y INT);
-- 2. 定义计算距离的函数
CREATE OR REPLACE FUNCTION distance(p1 point, p2 point)
RETURNS FLOAT AS $$ -- 返回浮点型(距离)
BEGIN
-- 勾股定理计算距离:√[(x1-x2)² + (y1-y2)²]
RETURN SQRT(POW(p1.x - p2.x, 2) + POW(p1.y - p2.y, 2));
END;
$$ LANGUAGE plpgsql;
-- 3. 使用函数(计算(0,0)到(3,4)的距离)
SELECT distance(ROW(0,0)::point, ROW(3,4)::point); -- 返回5.0
如果默认的B-tree索引不够用(比如要查“距离某点1公里内的商店”),PostgreSQL允许你用GiST或GIN索引。比如用GiST索引加速几何查询:
-- 1. 创建存储商店位置的表(用PostgreSQL内置的geometry类型)
CREATE TABLE shops (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
location geometry(Point, 4326) -- Point代表点,4326是坐标系(WGS84)
);
-- 2. 添加GiST索引(加速几何查询)
CREATE INDEX shops_location_idx ON shops USING GIST (location);
-- 3. 查询“天安门附近1公里内的商店”(116.403874, 39.914885是天安门的坐标)
SELECT name
FROM shops
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint(116.403874, 39.914885), 4326), -- 天安门的点
1000 -- 距离(米)
);
Q1:PostgreSQL作为ORDBMS,“对象”特性主要体现在哪里?
A1:支持用户自定义数据类型、函数、运算符等“对象”特性。比如你可以定义phone_number
类型(包含国家码、区号、号码),或定义distance
函数(像“对象的方法”一样计算距离)。
Q2:MVCC(多版本并发控制)解决了什么问题?
A2:解决了“读”和“写”互相阻塞的问题。每个事务看到的是数据的“快照”,比如你在查订单时,别人可以修改订单,你看到的还是修改前的状态,直到别人提交事务。高并发场景下(比如秒杀),读写可以同时进行,不会卡死。
Q3:PostgreSQL的触发器能做什么?举个例子。
A3:触发器可以在表发生插入、更新、删除时自动执行操作。比如当订单状态改为“完成”时,自动减少商品库存;或当用户注册时,自动发送欢迎邮件(需要结合外部服务)。
ERROR: relation "table_name" does not exist
原因:
orders
写成order
);sales
schema里,但你用了public
schema);解决办法:
dt
命令(psql客户端)查看当前schema下的表;SELECT * FROM sales.orders;
(如果表在sales
schema里);GRANT SELECT ON orders TO your_user;
给用户读权限。预防:
user_orders
而不是UserOrders
);public.orders
);SELECT
权限)。ERROR: insert or update on table "orders" violates foreign key constraint "orders_user_id_fkey"
原因:你插入的user_id
在users
表中不存在(比如users
表没有id=100
的用户,但你插入了user_id=100
的订单)。
解决办法:
user_id
是否存在:SELECT * FROM users WHERE id = 100;
;预防:
雷神 26.5 英寸显示器“DQ27F500E”首销:2K 500Hz QD-OLED,3999 元
15.88 万元起:奇瑞 iCar V23 赛博版汽车正式上市,可选单电机两驱 / 双电机四驱版