目录
一、数据库与对象管理
二、数据操作 (CRUD)
三、查询优化与执行计划分析
四、事务控制
五、数据类型与高级特性应用
六、系统查询与维护
研发中的重要注意事项
在 PostgreSQL 研发中,以下这些 SQL 应用是极其常见且核心的操作,涵盖了数据库设计、数据操作、查询优化和系统维护的关键方面:
一、数据库与对象管理
-
创建/修改表:
CREATE TABLE employees ( id SERIAL PRIMARY KEY, -- 自增主键 name VARCHAR(100) NOT NULL, email VARCHAR(255) UNIQUE, department_id INTEGER REFERENCES departments(id), -- 外键 salary NUMERIC(10, 2), hire_date DATE DEFAULT CURRENT_DATE, is_active BOOLEAN DEFAULT true, skills TEXT[] -- 数组类型 profile JSONB -- JSONB 类型存储灵活数据 ); -- 添加索引 CREATE INDEX idx_employees_department ON employees(department_id); CREATE INDEX idx_employees_name ON employees(name); CREATE INDEX idx_employees_profile_email ON employees USING gin ((profile->>'email')); -- JSONB 字段索引 -- 修改表 (添加列、修改列类型、添加约束等) ALTER TABLE employees ADD COLUMN phone VARCHAR(20); ALTER TABLE employees ALTER COLUMN salary TYPE DECIMAL(12, 2); ALTER TABLE employees ADD CONSTRAINT salary_check CHECK (salary > 0);
-
创建视图:
CREATE VIEW active_employees AS SELECT id, name, email, department_id FROM employees WHERE is_active = true; -- 物化视图 (定期刷新) CREATE MATERIALIZED VIEW department_salary_summary AS SELECT department_id, AVG(salary) AS avg_salary, SUM(salary) AS total_salary FROM employees GROUP BY department_id WITH DATA; REFRESH MATERIALIZED VIEW department_salary_summary; -- 手动刷新
-
创建函数/存储过程:
CREATE OR REPLACE FUNCTION get_employee_count(dept_id INTEGER) RETURNS INTEGER AS $$ DECLARE emp_count INTEGER; BEGIN SELECT COUNT(*) INTO emp_count FROM employees WHERE department_id = dept_id AND is_active = true; RETURN emp_count; END; $$ LANGUAGE plpgsql; -- 调用函数 SELECT get_employee_count(5);
二、数据操作 (CRUD)
-
插入数据:
INSERT INTO employees (name, email, department_id, salary) VALUES ('Alice Smith', 'alice@example.com', 3, 75000.00), ('Bob Johnson', 'bob@example.com', 3, 82000.00); -- 插入来自查询的结果 INSERT INTO archived_employees (id, name, email, leave_date) SELECT id, name, email, CURRENT_DATE FROM employees WHERE is_active = false;
-
查询数据:
- 基础查询:
SELECT id, name, salary FROM employees WHERE department_id = 3 AND salary > 80000;
- 连接:
SELECT e.name, d.name AS department_name, e.salary FROM employees e INNER JOIN departments d ON e.department_id = d.id; -- LEFT JOIN, RIGHT JOIN, FULL JOIN 也很常用
- 聚合与分组:
SELECT department_id, COUNT(*) AS num_employees, AVG(salary) AS avg_salary FROM employees WHERE is_active = true GROUP BY department_id HAVING AVG(salary) > 60000;
- 子查询:
SELECT name, salary FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);
- 窗口函数:
SELECT name, department_id, salary, RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_salary_rank, salary - LAG(salary) OVER (PARTITION BY department_id ORDER BY hire_date) AS salary_diff FROM employees;
- JSONB 查询:
SELECT name, profile->>'address' AS address -- 提取文本 FROM employees WHERE profile @> '{"skills": ["PostgreSQL"]}'; -- 包含特定键值对
- 基础查询:
-
更新数据:
UPDATE employees SET salary = salary * 1.05 -- 涨薪5% WHERE department_id = 3 AND performance_rating >= 4; -- 基于子查询更新 UPDATE employees e SET manager_id = (SELECT manager_id FROM departments d WHERE d.id = e.department_id) WHERE e.manager_id IS NULL;
-
删除数据:
DELETE FROM employees WHERE is_active = false AND hire_date < '2020-01-01'; -- 使用事务确保操作原子性 BEGIN; -- 先存档再删除 INSERT INTO deleted_employees_log SELECT *, CURRENT_TIMESTAMP FROM employees WHERE id = 123; DELETE FROM employees WHERE id = 123; COMMIT;
三、查询优化与执行计划分析
-
EXPLAIN
/EXPLAIN ANALYZE
:EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 456 AND order_date BETWEEN '2023-01-01' AND '2023-12-31' ORDER BY total_amount DESC LIMIT 10;
- 解读输出,查看是否使用了正确的索引、扫描类型(Seq Scan, Index Scan, Bitmap Heap Scan)、连接策略、排序方式、预估/实际行数、执行时间等。
- 这是优化慢查询的最重要工具。
-
强制使用索引 (通常不推荐,优化器通常更聪明):
SET enable_seqscan = off; -- 临时关闭顺序扫描 (测试用,勿在生产库长期开启) -- ... 运行查询 ... SET enable_seqscan = on;
-
创建/优化索引:
- 组合索引:
CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);
- 条件索引:
CREATE INDEX idx_active_high_salary ON employees(salary) WHERE is_active = true AND salary > 100000;
- 表达式索引:
CREATE INDEX idx_employee_lower_name ON employees(LOWER(name));
- 覆盖索引:
CREATE INDEX idx_order_summary ON orders(customer_id, order_date) INCLUDE (total_amount);
(PG 11+) GIN
/GiST
索引: 用于全文搜索(tsvector
)、数组、JSONB、几何类型等。
- 组合索引:
四、事务控制
-
显式事务:
BEGIN; -- 一系列操作 (INSERT, UPDATE, DELETE, SELECT ... FOR UPDATE) UPDATE accounts SET balance = balance - 100.00 WHERE id = 1; UPDATE accounts SET balance = balance + 100.00 WHERE id = 2; -- 根据业务逻辑决定提交或回滚 COMMIT; -- 或 ROLLBACK;
-
事务隔离级别:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 默认级别 -- 或 REPEATABLE READ, SERIALIZABLE BEGIN; ... -- 在指定隔离级别下执行事务 COMMIT;
-
SELECT ... FOR UPDATE
/SELECT ... FOR SHARE
:BEGIN; SELECT * FROM inventory WHERE product_id = 789 FOR UPDATE; -- 锁定该行,防止其他事务并发修改 -- 检查库存、扣减库存... UPDATE inventory SET stock = stock - 1 WHERE product_id = 789; COMMIT;
五、数据类型与高级特性应用
-
JSONB
操作:-- 插入 INSERT INTO products (id, name, attributes) VALUES (1, 'T-Shirt', '{"color": "red", "size": ["S", "M", "L"], "material": "cotton"}'); -- 查询 SELECT name, attributes->'size' AS available_sizes FROM products WHERE attributes @> '{"color": "red"}'; -- 包含 color=red -- 更新 UPDATE products SET attributes = jsonb_set(attributes, '{price}', '19.99') -- 添加/更新 price 键 WHERE id = 1; UPDATE products SET attributes = attributes || '{"discount": 0.1}' -- 合并添加 discount 键 WHERE id = 1;
-
数组操作:
SELECT name FROM employees WHERE 'PostgreSQL' = ANY(skills); -- 包含特定技能 UPDATE employees SET skills = array_append(skills, 'Docker') WHERE id = 456; -- 添加技能 SELECT unnest(skills) AS skill, COUNT(*) FROM employees GROUP BY skill; -- 展开数组统计
-
全文搜索:
-- 创建 tsvector 列和索引 ALTER TABLE articles ADD COLUMN search_vector tsvector; UPDATE articles SET search_vector = to_tsvector('english', title || ' ' || body); CREATE INDEX idx_articles_search ON articles USING gin(search_vector); -- 搜索 SELECT title, snippet(search_vector, query) AS highlight FROM articles, plainto_tsquery('english', 'database performance') query WHERE search_vector @@ query ORDER BY ts_rank(search_vector, query) DESC;
六、系统查询与维护
-
查看表/索引信息:
\d employees -- psql 元命令 SELECT * FROM pg_tables WHERE schemaname = 'public'; -- 系统表 SELECT * FROM pg_indexes WHERE tablename = 'employees'; -- 索引信息 SELECT pg_size_pretty(pg_total_relation_size('employees')); -- 表大小 (含索引)
-
查看连接/锁:
SELECT * FROM pg_stat_activity; -- 当前活动连接/查询 SELECT * FROM pg_locks; -- 当前锁信息
-
权限管理:
GRANT SELECT, INSERT, UPDATE ON employees TO developer_role; REVOKE DELETE ON employees FROM public;
研发中的重要注意事项
- 避免 N+1 查询: 在 ORM (如 Hibernate, Django ORM, ActiveRecord) 中尤其要注意。使用
JOIN
或批量查询 (WHERE ... IN (...)
) 代替在循环中执行单个查询。 - 参数化查询: 永远使用参数化查询或预处理语句来防止 SQL 注入攻击。不要拼接 SQL 字符串。
- 理解索引: 不是所有字段都适合加索引。索引有写入开销。理解查询模式,创建合适的索引(类型、字段顺序、条件)。定期分析索引使用情况 (
pg_stat_all_indexes
)。 - 事务边界: 明确事务的开始和结束。保持事务尽可能短小,减少锁竞争。正确处理并发冲突。
- 批量操作: 对于大批量插入/更新,使用
COPY
命令、批量INSERT ... VALUES (...), (...), ...
或INSERT ... SELECT ...
,比循环单条插入高效得多。 - 监控与分析: 使用
EXPLAIN ANALYZE
分析慢查询。监控数据库性能指标(连接数、锁、CPU、IO)。使用pg_stat_statements
扩展找出高频/高消耗 SQL。 - 连接池: 在应用层(如 HikariCP, pgBouncer)使用连接池管理数据库连接,避免频繁建立/断开连接的开销。
- 模式迁移: 使用成熟的迁移工具(如 Flyway, Liquibase, Alembic, Django Migrations)管理数据库模式变更,确保变更可追溯、可重复、可在不同环境一致执行。
掌握这些常规 SQL 应用并结合 PostgreSQL 的强大特性(如 JSONB、数组、GIN/GiST 索引、窗口函数、CTE、强大的 PL/pgSQL),能够高效、安全地进行应用研发,构建高性能、可扩展的后端系统。
这篇博客到这里就接近尾声了,希望我的分享能给您带来一些启发和帮助,别忘了点赞、收藏。您的每一次互动、鼓励是我持续创作的动力!期待与您再次相遇,共同探索更广阔的世界!