PostgreSQL 提供了非常丰富的数据类型系统,这是其区别于其他数据库的重要特性之一。
| 类型 | 存储大小 | 范围 | 使用场景 |
|---|
| SMALLINT | 2 字节 | -32768 ~ 32767 | 小范围整数、状态码 |
| INTEGER | 4 字节 | -2147483648 ~ 2147483647 | 常规整数、主键 |
| BIGINT | 8 字节 | -9223372036854775808 ~ 9223372036854775807 | 大数值、大表主键 |
CREATE TABLE products (
id INTEGER PRIMARY KEY,
status SMALLINT DEFAULT 1,
view_count BIGINT DEFAULT 0
);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
);
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY
);
SELECT currval('users_id_seq');
SELECT nextval('users_id_seq');
SELECT setval('users_id_seq', 1000);
CREATE TABLE financial (
amount NUMERIC(12, 2),
exchange_rate NUMERIC(10, 6),
precise_value NUMERIC
);
SELECT 'NaN'::NUMERIC;
SELECT 'Infinity'::NUMERIC;
SELECT 10.5::NUMERIC / 3;
SELECT 10.5 / 3;
CREATE TABLE measurements (
temperature REAL,
precise_value DOUBLE PRECISION
);
SELECT 0.1::REAL + 0.2::REAL;
SELECT 'Infinity'::DOUBLE PRECISION;
SELECT '-Infinity'::DOUBLE PRECISION;
SELECT 'NaN'::DOUBLE PRECISION;
| 类型 | 描述 | 最大长度 | 存储 |
|---|
| CHAR(n) | 定长,空格填充 | 10485760 | n 字节 |
| VARCHAR(n) | 变长,有长度限制 | 10485760 | 实际长度 + 1-4 字节 |
| TEXT | 变长,无长度限制 | 无限 | 实际长度 + 1-4 字节 |
CREATE TABLE example (
code CHAR(10),
name VARCHAR(100),
description TEXT
);
SELECT LENGTH('hello');
SELECT CHAR_LENGTH('你好');
SELECT OCTET_LENGTH('你好');
SELECT UPPER('hello');
SELECT LOWER('HELLO');
SELECT TRIM(' hello ');
SELECT LEFT('hello', 2);
SELECT RIGHT('hello', 2);
SELECT SUBSTRING('hello' FROM 2 FOR 3);
SHOW server_encoding;
SHOW client_encoding;
SET client_encoding TO 'UTF8';
CREATE TABLE chinese_data (
name TEXT COLLATE "zh_CN.UTF-8"
);
SELECT * FROM chinese_data ORDER BY name COLLATE "zh_CN.UTF-8";
| 类型 | 存储 | 范围 | 精度 |
|---|
| DATE | 4 字节 | 4713 BC ~ 5874897 AD | 天 |
| TIME | 8 字节 | 00:00:00 ~ 24:00:00 | 微秒 |
| TIMESTAMP | 8 字节 | 4713 BC ~ 294276 AD | 微秒 |
| TIMESTAMPTZ | 8 字节 | 4713 BC ~ 294276 AD | 微秒 |
| INTERVAL | 16 字节 | -178000000 年 ~ 178000000 年 | 微秒 |
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_date DATE,
start_time TIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
duration INTERVAL
);
INSERT INTO events (event_date, start_time, duration)
VALUES ('2024-01-15', '14:30:00', '2 hours 30 minutes');
SELECT
CURRENT_DATE,
CURRENT_TIME,
CURRENT_TIMESTAMP,
NOW(),
NOW() + INTERVAL '1 day',
NOW() - INTERVAL '1 month',
AGE(TIMESTAMP '2000-01-01'),
DATE_TRUNC('month', NOW())
;
SELECT
EXTRACT(YEAR FROM NOW()) AS year,
EXTRACT(MONTH FROM NOW()) AS month,
EXTRACT(DAY FROM NOW()) AS day,
EXTRACT(HOUR FROM NOW()) AS hour,
EXTRACT(DOW FROM NOW()) AS day_of_week,
EXTRACT(EPOCH FROM NOW()) AS unix_timestamp
;
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
SELECT TO_CHAR(NOW(), 'YYYY年MM月DD日');
SELECT TO_DATE('2024-01-15', 'YYYY-MM-DD');
SELECT TO_TIMESTAMP('2024-01-15 14:30:00', 'YYYY-MM-DD HH24:MI:SS');
SHOW timezone;
SET timezone TO 'Asia/Shanghai';
SET timezone TO 'UTC';
CREATE TABLE time_test (
ts TIMESTAMP,
tstz TIMESTAMPTZ
);
SET timezone TO 'Asia/Shanghai';
INSERT INTO time_test VALUES ('2024-01-15 12:00:00', '2024-01-15 12:00:00');
SET timezone TO 'UTC';
SELECT * FROM time_test;
SELECT
NOW() AT TIME ZONE 'UTC',
NOW() AT TIME ZONE 'America/New_York',
NOW() AT TIME ZONE 'Asia/Tokyo'
;
| 特性 | JSON | JSONB |
|---|
| 存储格式 | 文本 | 二进制 |
| 写入速度 | 快 | 略慢(需解析) |
| 查询速度 | 慢(需解析) | 快 |
| 保留格式 | 是 | 否 |
| 重复键 | 保留 | 只保留最后一个 |
| 索引支持 | 不支持 | 支持 GIN 索引 |
| 推荐 | 只存储 | 需要查询 |
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
attributes JSONB,
raw_data JSON
);
INSERT INTO products (name, attributes) VALUES
('iPhone 15', '{"color": "black", "storage": "128GB", "price": 6999}'),
('MacBook Pro', '{"color": "silver", "memory": "16GB", "storage": "512GB"}');
SELECT
name,
attributes->'color' AS color_json,
attributes->>'color' AS color_text,
attributes->>'price' AS price
FROM products;
SELECT attributes->'specs'->>'cpu' FROM products;
SELECT attributes->'tags'->>0 FROM products;
SELECT * FROM products WHERE attributes @> '{"color": "black"}';
SELECT * FROM products WHERE attributes ? 'color';
SELECT * FROM products WHERE attributes ?| ARRAY['color', 'size'];
SELECT * FROM products WHERE attributes ?& ARRAY['color', 'storage'];
SELECT attributes
SELECT attributes
SELECT * FROM products
WHERE jsonb_path_exists(attributes, '$.price ? (@ > 5000)');
SELECT jsonb_build_object('name', 'John', 'age', 30);
SELECT jsonb_build_array(1, 2, 3, 'four');
SELECT jsonb_agg(name) FROM products;
SELECT jsonb_object_agg(id, name) FROM products;
SELECT * FROM jsonb_each('{"a": 1, "b": 2}');
SELECT * FROM jsonb_each_text('{"a": 1, "b": 2}');
SELECT * FROM jsonb_array_elements('[1, 2, 3]');
SELECT * FROM jsonb_array_elements_text('["a", "b", "c"]');
UPDATE products SET attributes = attributes || '{"stock": 100}' WHERE id = 1;
UPDATE products SET attributes = attributes - 'old_key' WHERE id = 1;
UPDATE products SET attributes = jsonb_set(attributes, '{price}', '7999') WHERE id = 1;
UPDATE products SET attributes = attributes - 'color';
UPDATE products SET attributes = attributes
SELECT '{"a": 1}'::jsonb || '{"b": 2}'::jsonb;
SELECT jsonb_merge('{"a": {"b": 1}}'::jsonb, '{"a": {"c": 2}}'::jsonb);
CREATE INDEX idx_products_attributes ON products USING GIN (attributes);
CREATE INDEX idx_products_color ON products USING BTREE ((attributes->>'color'));
CREATE INDEX idx_products_price ON products USING BTREE (((attributes->>'price')::INTEGER));
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
tags TEXT[],
matrix INTEGER[][]
);
INSERT INTO articles (title, tags) VALUES
('PostgreSQL Guide', ARRAY['database', 'postgresql', 'tutorial']),
('Python Basics', '{"python", "programming", "beginner"}');
SELECT tags[1] FROM articles;
SELECT tags[1:2] FROM articles;
SELECT * FROM articles WHERE tags @> ARRAY['postgresql'];
SELECT * FROM articles WHERE tags && ARRAY['python', 'java'];
SELECT * FROM articles WHERE 'postgresql' = ANY(tags);
SELECT array_length(tags, 1) FROM articles;
SELECT cardinality(tags) FROM articles;
SELECT array_append(tags, 'new_tag') FROM articles;
SELECT array_prepend('first_tag', tags) FROM articles;
SELECT array_remove(tags, 'python') FROM articles;
SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
SELECT unnest(tags) FROM articles;
SELECT array_agg(DISTINCT tag) FROM (
SELECT unnest(tags) AS tag FROM articles
) t;
CREATE INDEX idx_articles_tags ON articles USING GIN (tags);
| 类型 | 描述 |
|---|
| INT4RANGE | INTEGER 范围 |
| INT8RANGE | BIGINT 范围 |
| NUMRANGE | NUMERIC 范围 |
| TSRANGE | TIMESTAMP 范围 |
| TSTZRANGE | TIMESTAMPTZ 范围 |
| DATERANGE | DATE 范围 |
CREATE TABLE reservations (
id SERIAL PRIMARY KEY,
room_id INTEGER,
during TSTZRANGE,
EXCLUDE USING GIST (room_id WITH =, during WITH &&)
);
INSERT INTO reservations (room_id, during) VALUES
(1, '[2024-01-15 14:00, 2024-01-15 16:00)'),
(1, '[2024-01-15 16:00, 2024-01-15 18:00)');
SELECT
'[1,5)'::INT4RANGE @> 3,
'[1,5)'::INT4RANGE @> '[2,4)'::INT4RANGE,
'[1,5)'::INT4RANGE && '[3,7)'::INT4RANGE,
'[1,5)'::INT4RANGE << '[6,10)'::INT4RANGE,
'[6,10)'::INT4RANGE >> '[1,5)'::INT4RANGE,
'[1,5)'::INT4RANGE -|- '[5,10)'::INT4RANGE
;
SELECT
lower('[1,5)'::INT4RANGE),
upper('[1,5)'::INT4RANGE),
isempty('empty'::INT4RANGE),
lower_inc('[1,5)'::INT4RANGE),
upper_inc('[1,5)'::INT4RANGE)
;
SELECT
'[1,5)'::INT4RANGE * '[3,7)'::INT4RANGE,
'[1,5)'::INT4RANGE + '[3,7)'::INT4RANGE
;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE sessions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id INTEGER,
created_at TIMESTAMPTZ DEFAULT NOW()
);
SELECT uuid_generate_v4();
SELECT uuid_generate_v1();
SELECT gen_random_uuid();
SELECT '550e8400-e29b-41d4-a716-446655440000'::UUID;
CREATE TABLE network_logs (
id SERIAL PRIMARY KEY,
client_ip INET,
network CIDR,
mac_address MACADDR
);
INSERT INTO network_logs (client_ip, network, mac_address) VALUES
('192.168.1.100', '192.168.1.0/24', '08:00:2b:01:02:03');
SELECT
'192.168.1.0/24'::INET >> '192.168.1.100'::INET,
'192.168.1.100'::INET << '192.168.1.0/24'::INET,
masklen('192.168.1.0/24'::INET),
host('192.168.1.100/24'::INET),
network('192.168.1.100/24'::INET)
;
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'delivered', 'cancelled');
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
status order_status DEFAULT 'pending'
);
INSERT INTO orders (status) VALUES ('paid');
SELECT enum_range(NULL::order_status);
ALTER TYPE order_status ADD VALUE 'refunded' AFTER 'cancelled';
SELECT * FROM orders ORDER BY status;
CREATE TABLE users (
id SERIAL PRIMARY KEY
);
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY
);
CREATE TABLE distributed_data (
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
);
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
quantity INTEGER,
PRIMARY KEY (order_id, product_id)
);
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
status SMALLINT DEFAULT 1,
is_verified BOOLEAN DEFAULT FALSE,
profile JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMPTZ,
CONSTRAINT users_username_unique UNIQUE (username),
CONSTRAINT users_email_unique UNIQUE (email),
CONSTRAINT users_username_length CHECK (LENGTH(username) >= 3),
CONSTRAINT users_status_valid CHECK (status IN (0, 1, 2))
);
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_users_status ON users (status) WHERE deleted_at IS NULL;
CREATE INDEX idx_users_created_at ON users (created_at DESC);
CREATE INDEX idx_users_profile ON users USING GIN (profile);
COMMENT ON TABLE users IS '用户表';
COMMENT ON COLUMN users.id IS '用户ID';
COMMENT ON COLUMN users.status IS '状态:0-禁用,1-正常,2-待审核';
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id),
total_amount NUMERIC(12, 2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT REFERENCES orders(id),
product_id BIGINT REFERENCES products(id),
quantity INTEGER,
unit_price NUMERIC(10, 2)
);
CREATE TABLE orders_denormalized (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT,
user_name VARCHAR(50),
user_email VARCHAR(100),
items JSONB,
total_amount NUMERIC(12, 2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE MATERIALIZED VIEW order_summary AS
SELECT
o.id,
o.user_id,
u.username,
u.email,
o.total_amount,
COALESCE(jsonb_agg(jsonb_build_object(
'product_id', oi.product_id,
'quantity', oi.quantity,
'unit_price', oi.unit_price
)), '[]') AS items
FROM orders o
JOIN users u ON o.user_id = u.id
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id, u.id;
REFRESH MATERIALIZED VIEW order_summary;
REFRESH MATERIALIZED VIEW CONCURRENTLY order_summary;
CREATE TABLE logs (
id BIGSERIAL,
message TEXT,
created_at TIMESTAMPTZ NOT NULL
) PARTITION BY RANGE (created_at);
CREATE TABLE logs_2024_01 PARTITION OF logs
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE logs_2024_02 PARTITION OF logs
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
CREATE TABLE logs_default PARTITION OF logs DEFAULT;
CREATE TABLE orders_partitioned (
id BIGSERIAL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMPTZ
) PARTITION BY LIST (status);
CREATE TABLE orders_pending PARTITION OF orders_partitioned
FOR VALUES IN ('pending');
CREATE TABLE orders_completed PARTITION OF orders_partitioned
FOR VALUES IN ('paid', 'shipped', 'delivered');
CREATE TABLE orders_cancelled PARTITION OF orders_partitioned
FOR VALUES IN ('cancelled', 'refunded');
CREATE TABLE data (
id BIGINT NOT NULL,
content TEXT
) PARTITION BY HASH (id);
CREATE TABLE data_0 PARTITION OF data FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE data_1 PARTITION OF data FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE data_2 PARTITION OF data FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE data_3 PARTITION OF data FOR VALUES WITH (MODULUS 4, REMAINDER 3);
| 场景 | 推荐类型 |
|---|
| 主键 | BIGSERIAL 或 UUID |
| 金额 | NUMERIC(12, 2) |
| 状态 | SMALLINT 或 ENUM |
| 字符串 | TEXT(无长度限制时)或 VARCHAR(n) |
| 时间戳 | TIMESTAMPTZ |
| 扩展属性 | JSONB |
| 标签/分类 | TEXT[] 或 JSONB |
| 时间范围 | TSTZRANGE |
| IP 地址 | INET |
| 布尔值 | BOOLEAN |
- 始终使用主键:BIGSERIAL 或 UUID
- 添加时间戳:created_at, updated_at
- 使用合适类型:不要用 VARCHAR 存数字
- 添加注释:COMMENT ON 记录字段含义
- 建立索引:根据查询模式建立
- 考虑分区:大表使用分区提高性能
- 适度反范式:在性能需要时使用 JSONB