SQL (Structured Query Language) 是用于管理关系型数据库的标准查询语言。本文按照 SQL 标准语法,系统介绍数据定义(DDL)、数据操作(DML)、数据查询(DQL)和数据控制(DCL)四大核心语法类别,帮助读者掌握 SQL 的基础语法规则和使用方法。
SQL 标准的演进
SQL 由国际标准化组织 ISO 和美国国家标准协会 ANSI 共同制定和维护。标准持续演进,主要版本包括 SQL-86、SQL-92、SQL:1999、SQL:2003、SQL:2011、SQL:2016 等,每个版本都引入新特性以适应数据库技术的发展。
虽然各数据库厂商(如 PostgreSQL、MySQL、Oracle、SQL Server)在实现上有所差异,但它们都遵循 SQL 标准的核心语法。理解标准语法有助于编写可移植的 SQL 代码。
SQL 标准文档来源:
-
ISO/IEC 9075 标准:SQL 的官方标准由 ISO/IEC JTC 1/SC 32 维护,可以从 ISO 官网购买正式文档
- ISO 官网:www.iso.org/standard/76...
- 标准编号:ISO/IEC 9075(多个部分)
-
SQL 标准参考资料:
- Modern SQL 网站:modern-sql.com/ (介绍 SQL 标准演进)
- Wikipedia SQL 标准页面:en.wikipedia.org/wiki/SQL
本文基于 SQL 标准语法编写,示例代码在遵循标准的数据库(如 PostgreSQL)中均可运行。
SQL 中的重点概念
在深入学习 SQL 语法之前,理解一些核心概念有助于更好地掌握 SQL 的使用。本章将 SQL 中的重要概念按类别进行介绍。
数据库对象层次结构
SQL 数据库采用层次化的组织结构,从上到下依次为:
Database(数据库)→ Schema(模式)→ Table/View/Function(表/视图/函数等)→ Column/Row(列/行)
Database(数据库)
数据库是所有数据和对象的最顶层容器。一个数据库服务器可以包含多个数据库。
sql
-- 创建数据库
CREATE DATABASE company;
-- 连接到数据库(语法因数据库而异)
\c company -- PostgreSQL
USE company; -- MySQL
-- 删除数据库
DROP DATABASE company;
延伸阅读:
- PostgreSQL 数据库文档:www.postgresql.org/docs/curren...
Schema(模式)
Schema 是数据库中对象的逻辑容器,类似于文件系统中的文件夹。一个数据库可以包含多个 Schema,每个 Schema 可以包含表、视图、函数等对象。
Schema 的作用:
- 组织数据库对象:将相关对象分组管理
- 命名空间隔离:不同 Schema 中可以有同名的表
- 权限控制:可以对整个 Schema 授权
sql
-- 创建 Schema
CREATE SCHEMA sales;
CREATE SCHEMA hr;
-- 在 Schema 中创建表
CREATE TABLE sales.orders (
id INTEGER PRIMARY KEY,
total_amount DECIMAL(10, 2)
);
-- 使用 Schema
SELECT * FROM sales.orders;
-- 设置当前 Schema(搜索路径)
SET search_path TO sales, public;
SELECT * FROM orders; -- 自动查找 sales.orders
-- 删除 Schema
DROP SCHEMA sales CASCADE; -- CASCADE 删除 Schema 中的所有对象
延伸阅读:
- PostgreSQL Schema 文档:www.postgresql.org/docs/curren...
Table(表)
表是存储数据的基本单位,由行(记录)和列(字段)组成。表是关系型数据库的核心概念。
表的组成:
- Column(列):定义数据的结构,每列有名称和数据类型
- Row(行):表中的一条记录,包含每列的具体值
- 约束(Constraint):定义列或表的规则(如主键、外键、唯一性等)
sql
-- 创建表
CREATE TABLE users (
id INTEGER PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 查看表结构(PostgreSQL)
\d users
-- 修改表结构
ALTER TABLE users ADD COLUMN age INTEGER;
-- 删除表
DROP TABLE users;
延伸阅读:
- PostgreSQL 表文档:www.postgresql.org/docs/curren...
虚拟对象和辅助对象
View(视图)
视图是基于查询结果的虚拟表。视图不存储实际数据,而是存储查询定义,每次访问视图时动态执行查询。
视图的作用:
- 简化复杂查询:将复杂的 JOIN 和聚合封装成视图
- 数据安全:隐藏敏感列,只暴露部分数据
- 逻辑数据独立性:应用程序依赖视图而非表,表结构变化时只需修改视图定义
sql
-- 创建视图
CREATE VIEW active_users AS
SELECT id, username, email, created_at
FROM users
WHERE is_active = TRUE;
-- 使用视图(像使用表一样)
SELECT * FROM active_users;
-- 删除视图
DROP VIEW active_users;
延伸阅读:
- PostgreSQL 视图文档:www.postgresql.org/docs/curren...
- 物化视图 (Materialized View):存储查询结果的视图,需要手动刷新
Index(索引)
索引是数据库用于加速数据检索的数据结构。索引类似于书的目录,可以快速定位数据位置。
索引类型:
- B-Tree 索引:默认索引类型,适用于等值查询和范围查询
- Hash 索引:只支持等值查询
- GiST 索引:通用搜索树,适用于几何数据
- GIN 索引:倒排索引,适用于全文搜索和数组查询
sql
-- 创建普通索引
CREATE INDEX idx_users_email ON users(email);
-- 创建唯一索引
CREATE UNIQUE INDEX idx_users_username ON users(username);
-- 创建复合索引
CREATE INDEX idx_orders_user_date ON orders(user_id, order_date);
-- 创建部分索引(条件索引)
CREATE INDEX idx_active_users ON users(email) WHERE is_active = TRUE;
-- 删除索引
DROP INDEX idx_users_email;
索引使用原则:
- 在经常作为查询条件的列上创建索引(WHERE、JOIN)
- 索引加速查询但会降低写入性能
- 避免过度索引,每个索引都会占用存储空间
延伸阅读:
- PostgreSQL 索引文档:www.postgresql.org/docs/curren...
- MySQL 索引文档:dev.mysql.com/doc/refman/...
Sequence(序列)
序列是自动生成唯一数字的数据库对象,常用于生成主键值。
sql
-- 创建序列
CREATE SEQUENCE user_id_seq
START WITH 1
INCREMENT BY 1;
-- 获取下一个值
SELECT nextval('user_id_seq');
-- 获取当前值(不增加)
SELECT currval('user_id_seq');
-- 在表中使用序列
CREATE TABLE users (
id INTEGER DEFAULT nextval('user_id_seq') PRIMARY KEY,
username VARCHAR(50)
);
-- 或者使用 SERIAL 类型(PostgreSQL 自动创建序列)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50)
);
-- 删除序列
DROP SEQUENCE user_id_seq;
延伸阅读:
- PostgreSQL 序列文档:www.postgresql.org/docs/curren...
可执行对象
Function(函数)
函数是返回单个值的可重用代码块,可以在 SELECT 语句中使用。
sql
-- 创建函数(PostgreSQL)
CREATE FUNCTION calculate_discount(price DECIMAL, discount_rate DECIMAL)
RETURNS DECIMAL AS $$
BEGIN
RETURN price * (1 - discount_rate);
END;
$$ LANGUAGE plpgsql;
-- 使用函数
SELECT calculate_discount(100, 0.1); -- 返回 90
-- 在查询中使用函数
SELECT id, name, price, calculate_discount(price, 0.1) AS discounted_price
FROM products;
-- 删除函数
DROP FUNCTION calculate_discount;
延伸阅读:
- PostgreSQL 函数文档:www.postgresql.org/docs/curren...
Stored Procedure(存储过程)
存储过程可以执行复杂逻辑,可以返回多个结果集或不返回值。
存储过程 vs 函数:
- 函数:返回单个值,可以在 SELECT 语句中使用
- 存储过程:可以执行复杂逻辑,可以返回多个结果集或不返回值
sql
-- 创建存储过程(PostgreSQL 11+)
CREATE PROCEDURE transfer_funds(
from_account INTEGER,
to_account INTEGER,
amount DECIMAL
)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE accounts SET balance = balance - amount WHERE id = from_account;
UPDATE accounts SET balance = balance + amount WHERE id = to_account;
COMMIT;
END;
$$;
-- 调用存储过程
CALL transfer_funds(1, 2, 100);
-- 删除存储过程
DROP PROCEDURE transfer_funds;
延伸阅读:
- PostgreSQL 存储过程文档:www.postgresql.org/docs/curren...
- MySQL 存储程序文档:dev.mysql.com/doc/refman/...
Trigger(触发器)
触发器是在特定事件(INSERT、UPDATE、DELETE)发生时自动执行的数据库对象。触发器用于维护数据完整性、审计日志、自动更新关联数据等。
触发器类型:
- BEFORE 触发器:在事件执行前触发,可以修改即将写入的数据
- AFTER 触发器:在事件执行后触发,适合审计日志
- INSTEAD OF 触发器:替代原操作,常用于视图
sql
-- 创建触发器函数(PostgreSQL)
CREATE OR REPLACE FUNCTION update_modified_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 创建触发器
CREATE TRIGGER trigger_update_timestamp
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_modified_timestamp();
-- 删除触发器
DROP TRIGGER trigger_update_timestamp ON users;
触发器使用场景:
- 自动更新时间戳
- 记录审计日志
- 验证数据完整性
- 级联更新关联数据
注意事项:
- 触发器会影响性能,谨慎使用
- 触发器逻辑应简单明确
- 避免触发器之间的循环调用
延伸阅读:
- PostgreSQL 触发器文档:www.postgresql.org/docs/curren...
- MySQL 触发器文档:dev.mysql.com/doc/refman/...
数据完整性和约束
Constraint(约束)
约束用于确保数据的完整性和一致性。主要约束类型包括:
PRIMARY KEY(主键约束):
- 唯一标识表中的每一行
- 不允许 NULL 值
- 一个表只能有一个主键(可以是单列或多列组合)
FOREIGN KEY(外键约束):
- 建立表之间的关联关系
- 外键列的值必须在被引用表的主键或唯一键中存在(或为 NULL)
- 维护参照完整性
级联操作:
ON DELETE CASCADE:删除父表记录时,自动删除子表关联记录ON DELETE SET NULL:删除父表记录时,子表外键设为 NULLON DELETE RESTRICT:如果存在子表关联记录,禁止删除父表记录ON UPDATE CASCADE:更新父表主键时,自动更新子表外键
sql
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
UNIQUE(唯一约束):
- 确保列中的值唯一
- 允许 NULL 值(NULL 不等于 NULL)
CHECK(检查约束):
- 限制列值必须满足指定条件
NOT NULL(非空约束):
- 确保列不能存储 NULL 值
DEFAULT(默认值约束):
- 为列指定默认值
延伸阅读:
- PostgreSQL 约束文档:www.postgresql.org/docs/curren...
高级查询特性
Window Functions(窗口函数)
窗口函数在一组相关的行上执行计算,但不会像 GROUP BY 那样将行合并为单行。窗口函数保留原始行,同时提供聚合或分析结果。
常用窗口函数:
- 排名函数 :
ROW_NUMBER()、RANK()、DENSE_RANK() - 聚合函数 :
SUM()、AVG()、COUNT()、MAX()、MIN() - 偏移函数 :
LAG()、LEAD()、FIRST_VALUE()、LAST_VALUE()
sql
-- ROW_NUMBER:为每行分配唯一序号
SELECT
username,
score,
ROW_NUMBER() OVER (ORDER BY score DESC) AS rank
FROM users;
-- PARTITION BY:分组计算
SELECT
department,
username,
salary,
AVG(salary) OVER (PARTITION BY department) AS dept_avg_salary
FROM employees;
-- LAG:访问前一行的值
SELECT
order_date,
total_amount,
LAG(total_amount) OVER (ORDER BY order_date) AS previous_amount
FROM orders;
窗口函数 vs GROUP BY:
GROUP BY将多行聚合为一行- 窗口函数保留所有行,在每行上添加计算结果
延伸阅读:
- PostgreSQL 窗口函数文档:www.postgresql.org/docs/curren...
- MySQL 窗口函数文档:dev.mysql.com/doc/refman/...
- Modern SQL 窗口函数教程:modern-sql.com/feature/ove...
事务和并发控制
Transaction(事务)
事务是一组 SQL 语句的逻辑执行单元,要么全部成功,要么全部失败。
ACID 特性:
- 原子性 (Atomicity):事务中的所有操作要么全部完成,要么全部不完成
- 一致性 (Consistency):事务执行前后,数据库从一个一致性状态转换到另一个一致性状态
- 隔离性 (Isolation):多个事务并发执行时,互不干扰
- 持久性 (Durability):事务提交后,对数据的修改是永久的
事务控制语句:
BEGIN/START TRANSACTION:开始事务COMMIT:提交事务ROLLBACK:回滚事务SAVEPOINT:设置保存点
Isolation Level(隔离级别)
SQL 标准定义了四种事务隔离级别:
READ UNCOMMITTED:读未提交(可能出现脏读)READ COMMITTED:读已提交REPEATABLE READ:可重复读SERIALIZABLE:串行化
权限和安全
User(用户)
用户是数据库的访问主体,可以登录数据库的实体,具有身份验证信息(如密码)。
Role(角色)
角色是一组权限的集合,不一定能登录,主要用于权限管理。
角色 vs 用户:
- 用户:可以登录数据库,具有密码
- 角色:权限集合,可以授予用户或其他角色
Privilege(权限)
权限控制用户对数据库对象的访问和操作,包括:
SELECT、INSERT、UPDATE、DELETE:数据操作权限CREATE、ALTER、DROP:对象定义权限EXECUTE:执行函数或存储过程的权限USAGE:使用 Schema 或序列的权限CONNECT:连接数据库的权限TRIGGER:创建触发器的权限
概念总结
| 分类 | 概念 | 说明 |
|---|---|---|
| 层次结构 | Database | 最顶层容器 |
| Schema | 逻辑命名空间 | |
| Table | 存储数据的基本单位 | |
| Column | 列/字段 | |
| Row | 行/记录 | |
| 虚拟和辅助 | View | 虚拟表 |
| Index | 加速检索 | |
| Sequence | 自增序列 | |
| 可执行对象 | Function | 返回值的函数 |
| Stored Procedure | 存储过程 | |
| Trigger | 自动触发的操作 | |
| 数据完整性 | Primary Key | 主键约束 |
| Foreign Key | 外键约束 | |
| Unique | 唯一约束 | |
| Check | 检查约束 | |
| Not Null | 非空约束 | |
| Default | 默认值约束 | |
| 查询特性 | Window Functions | 窗口函数 |
| Subquery | 子查询 | |
| CTE | 公共表表达式 | |
| Join | 表连接 | |
| 事务控制 | Transaction | 事务 |
| Isolation Level | 隔离级别 | |
| Savepoint | 保存点 | |
| 权限安全 | User | 用户 |
| Role | 角色 | |
| Privilege | 权限 |
这些概念构成了 SQL 数据库的完整体系。理解它们有助于设计高效、可维护的数据库应用。
SQL 基础语法规则
SQL 语法规则定义了如何编写合法的 SQL 语句。掌握这些基础规则是编写正确、可读的 SQL 代码的前提。
语法约定
大小写:
- SQL 关键字(如
SELECT、FROM、WHERE)不区分大小写,但惯例上使用大写以提高可读性 - 标识符(表名、列名)在标准中不区分大小写,但某些数据库(如 PostgreSQL)在使用双引号时区分大小写
sql
-- 以下三种写法等价
SELECT name FROM users;
select name from users;
Select Name From Users;
语句分隔符:
- 每条 SQL 语句以分号
;结尾 - 单条语句执行时可以省略分号,但多条语句必须使用分号分隔
sql
-- 单条语句
SELECT * FROM users;
-- 多条语句
SELECT * FROM users;
SELECT * FROM orders;
注释:
- 单行注释:使用
--开头 - 多行注释:使用
/* */包围
sql
-- 这是单行注释
SELECT * FROM users; -- 也可以在语句后添加注释
/*
这是多行注释
可以跨越多行
*/
SELECT * FROM orders;
命名规范
标识符规则:
- 标识符(表名、列名、索引名等)必须以字母或下划线开头
- 可以包含字母、数字、下划线
- 长度限制因数据库而异,标准建议不超过 128 字符
- 避免使用 SQL 关键字作为标识符
sql
-- 合法的标识符
user_name
User123
_temp_table
-- 不推荐的标识符(关键字)
select
table
order
-- 使用双引号可以使用关键字或特殊字符(不推荐)
"select"
"user-name"
数据类型
SQL 标准定义了以下主要数据类型:
数值类型:
INTEGER/INT:整数SMALLINT:小整数BIGINT:大整数DECIMAL(p, s)/NUMERIC(p, s):定点数,p 为精度,s 为小数位数REAL:单精度浮点数DOUBLE PRECISION:双精度浮点数
字符类型:
CHAR(n)/CHARACTER(n):定长字符串,不足 n 个字符会用空格填充VARCHAR(n)/CHARACTER VARYING(n):变长字符串,最大长度 nTEXT:可变长文本(非标准,但广泛支持)
日期时间类型:
DATE:日期(年-月-日)TIME:时间(时:分:秒)TIMESTAMP:时间戳(日期 + 时间)INTERVAL:时间间隔
布尔类型:
BOOLEAN:布尔值,取值为TRUE或FALSE
其他类型:
BLOB:二进制大对象CLOB:字符大对象
| 数据类型 | 说明 | 示例 |
|---|---|---|
INTEGER |
整数 | 123, -456 |
DECIMAL(10,2) |
定点数 | 123.45 |
VARCHAR(50) |
变长字符串 | 'Hello' |
DATE |
日期 | '2025-12-10' |
TIMESTAMP |
时间戳 | '2025-12-10 14:30:00' |
BOOLEAN |
布尔值 | TRUE, FALSE |
字面量表示
字符串字面量:
- 使用单引号
'包围 - 单引号本身需要转义为两个单引号
''
sql
SELECT 'Hello World';
SELECT 'It''s a test'; -- 输出: It's a test
数值字面量:
- 整数直接书写
- 小数使用点号
.分隔 - 科学计数法使用
E或e
sql
SELECT 123;
SELECT 123.45;
SELECT 1.23E2; -- 123
日期时间字面量:
- 使用标准格式字符串,配合类型转换
sql
SELECT DATE '2025-12-10';
SELECT TIME '14:30:00';
SELECT TIMESTAMP '2025-12-10 14:30:00';
布尔字面量:
sql
SELECT TRUE;
SELECT FALSE;
NULL 值:
NULL表示缺失或未知的值NULL不等于任何值(包括它自己)
sql
SELECT NULL;
SELECT column_name IS NULL; -- 检查是否为 NULL
SELECT column_name IS NOT NULL; -- 检查是否不为 NULL
数据定义语言 (DDL)
DDL (Data Definition Language) 用于定义和管理数据库对象的结构,如数据库、表、索引等。DDL 语句会影响数据库的架构,而不直接操作数据本身。
CREATE 语句
CREATE 用于创建数据库对象。
创建数据库:
sql
CREATE DATABASE company;
创建表:
sql
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
department VARCHAR(50),
salary DECIMAL(10, 2),
hire_date DATE
);
创建索引:
sql
-- 创建普通索引
CREATE INDEX idx_department ON employees(department);
-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON employees(email);
ALTER 语句
ALTER 用于修改已存在的数据库对象结构。
添加列:
sql
ALTER TABLE employees
ADD COLUMN phone VARCHAR(20);
修改列类型:
sql
ALTER TABLE employees
ALTER COLUMN salary TYPE DECIMAL(12, 2);
删除列:
sql
ALTER TABLE employees
DROP COLUMN phone;
添加约束:
sql
ALTER TABLE employees
ADD CONSTRAINT check_salary CHECK (salary > 0);
DROP 语句
DROP 用于删除数据库对象。删除操作不可恢复,需谨慎使用。
删除表:
sql
DROP TABLE employees;
删除数据库:
sql
DROP DATABASE company;
删除索引:
sql
DROP INDEX idx_department;
条件删除(如果存在才删除):
sql
DROP TABLE IF EXISTS employees;
TRUNCATE 语句
TRUNCATE 用于快速清空表中的所有数据,但保留表结构。
sql
TRUNCATE TABLE employees;
TRUNCATE 与 DELETE 的区别:
TRUNCATE是 DDL 操作,直接释放表空间,速度快,不记录单行删除日志DELETE是 DML 操作,逐行删除数据,速度慢,可以回滚TRUNCATE会重置自增计数器,DELETE不会
约束类型
约束 (Constraint) 用于限制表中数据的规则,确保数据的完整性和一致性。
PRIMARY KEY(主键约束):
- 唯一标识表中的每一行
- 不允许 NULL 值
- 一个表只能有一个主键(可以是单列或多列组合)
sql
CREATE TABLE users (
id INTEGER PRIMARY KEY,
username VARCHAR(50)
);
-- 多列组合主键
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
quantity INTEGER,
PRIMARY KEY (order_id, product_id)
);
FOREIGN KEY(外键约束):
- 建立表之间的关联关系
- 外键列的值必须在被引用表的主键或唯一键中存在(或为 NULL)
- 维护参照完整性
sql
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
order_date DATE,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 指定级联操作
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE -- 删除用户时同时删除其订单
ON UPDATE CASCADE -- 更新用户 ID 时同时更新订单中的 user_id
);
UNIQUE(唯一约束):
- 确保列中的值唯一
- 允许 NULL 值(NULL 不等于 NULL)
- 一个表可以有多个唯一约束
sql
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email VARCHAR(100) UNIQUE,
username VARCHAR(50) UNIQUE
);
CHECK(检查约束):
- 限制列值必须满足指定条件
- 可以基于单列或多列
sql
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
age INTEGER CHECK (age >= 18),
salary DECIMAL(10, 2) CHECK (salary > 0),
start_date DATE,
end_date DATE,
CHECK (end_date > start_date) -- 多列约束
);
NOT NULL(非空约束):
- 确保列不能存储 NULL 值
- 必须在插入或更新时提供值
sql
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL
);
DEFAULT(默认值约束):
- 为列指定默认值
- 插入数据时如果未提供该列的值,使用默认值
sql
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
数据查询语言 (DQL)
DQL (Data Query Language) 用于从数据库中检索数据。SELECT 是 DQL 的核心语句,也是 SQL 中使用最频繁的语句。
SELECT 基本语法结构
SELECT 语句的完整语法结构如下:
sql
SELECT [DISTINCT] column1, column2, ...
FROM table_name
[WHERE condition]
[GROUP BY column1, column2, ...]
[HAVING condition]
[ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...]
[LIMIT count [OFFSET offset]];
简单查询:
sql
-- 查询所有列
SELECT * FROM users;
-- 查询指定列
SELECT id, username, email FROM users;
-- 使用别名
SELECT id AS user_id, username AS name FROM users;
-- 去重查询
SELECT DISTINCT department FROM employees;
WHERE 子句
WHERE 子句用于筛选满足条件的记录。
比较运算符:
sql
-- 等于
SELECT * FROM products WHERE price = 99.99;
-- 不等于
SELECT * FROM products WHERE price != 99.99;
SELECT * FROM products WHERE price <> 99.99; -- 标准写法
-- 大于、小于
SELECT * FROM products WHERE price > 100;
SELECT * FROM products WHERE price <= 50;
-- BETWEEN
SELECT * FROM products WHERE price BETWEEN 50 AND 100; -- 包含 50 和 100
-- IN
SELECT * FROM users WHERE status IN ('active', 'pending');
-- LIKE(模糊匹配)
SELECT * FROM users WHERE username LIKE 'admin%'; -- 以 admin 开头
SELECT * FROM users WHERE email LIKE '%@gmail.com'; -- 以 @gmail.com 结尾
SELECT * FROM users WHERE username LIKE '%test%'; -- 包含 test
SELECT * FROM users WHERE username LIKE '_dmin'; -- _ 匹配单个字符
-- IS NULL / IS NOT NULL
SELECT * FROM users WHERE phone IS NULL;
SELECT * FROM users WHERE email IS NOT NULL;
逻辑运算符:
sql
-- AND
SELECT * FROM products WHERE price > 50 AND stock_quantity > 0;
-- OR
SELECT * FROM users WHERE status = 'active' OR status = 'pending';
-- NOT
SELECT * FROM users WHERE NOT status = 'banned';
-- 组合使用(使用括号控制优先级)
SELECT * FROM products
WHERE (category = 'electronics' OR category = 'books')
AND price < 100;
JOIN 连接
JOIN 用于关联多个表的数据。理解不同类型的 JOIN 对于编写复杂查询至关重要。
INNER JOIN(内连接):
- 返回两个表中满足连接条件的记录
- 只返回匹配的行
sql
-- 查询用户及其订单信息
SELECT users.username, orders.id, orders.total_amount
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- 简写(省略 INNER)
SELECT u.username, o.id, o.total_amount
FROM users u
JOIN orders o ON u.id = o.user_id;
LEFT JOIN(左外连接):
- 返回左表的所有记录
- 如果右表没有匹配的记录,右表列显示为 NULL
sql
-- 查询所有用户及其订单(包括没有订单的用户)
SELECT u.username, o.id, o.total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
RIGHT JOIN(右外连接):
- 返回右表的所有记录
- 如果左表没有匹配的记录,左表列显示为 NULL
sql
-- 查询所有订单及其用户(包括用户已删除的订单)
SELECT u.username, o.id, o.total_amount
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
FULL OUTER JOIN(全外连接):
- 返回两个表的所有记录
- 没有匹配的记录对应列显示为 NULL
sql
-- 查询所有用户和所有订单(包括没有订单的用户和没有用户的订单)
SELECT u.username, o.id, o.total_amount
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
CROSS JOIN(交叉连接):
- 返回两个表的笛卡尔积
- 左表每行与右表每行组合
sql
-- 生成所有颜色和尺寸的组合
SELECT colors.name AS color, sizes.name AS size
FROM colors
CROSS JOIN sizes;
多表连接:
sql
-- 查询用户、订单、订单明细
SELECT u.username, o.id AS order_id, p.name AS product_name, oi.quantity
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;
GROUP BY 和聚合函数
GROUP BY 用于按一个或多个列对结果进行分组,通常与聚合函数配合使用。
聚合函数:
sql
-- COUNT:计数
SELECT COUNT(*) FROM users; -- 统计总行数
SELECT COUNT(phone) FROM users; -- 统计非 NULL 的 phone 数量
SELECT COUNT(DISTINCT department) FROM employees; -- 统计不重复的部门数
-- SUM:求和
SELECT SUM(total_amount) FROM orders;
-- AVG:平均值
SELECT AVG(salary) FROM employees;
-- MAX / MIN:最大值 / 最小值
SELECT MAX(price) FROM products;
SELECT MIN(hire_date) FROM employees;
GROUP BY 分组:
sql
-- 按部门统计员工数量
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department;
-- 按用户统计订单总额
SELECT user_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY user_id;
-- 多列分组
SELECT department, status, COUNT(*) AS count
FROM employees
GROUP BY department, status;
-- 使用聚合函数计算百分比
SELECT
department,
COUNT(*) AS count,
COUNT(*) * 100.0 / (SELECT COUNT(*) FROM employees) AS percentage
FROM employees
GROUP BY department;
HAVING 子句
HAVING 用于对分组后的结果进行筛选。它与 WHERE 的区别在于:
WHERE在分组前筛选行HAVING在分组后筛选组
sql
-- 查询订单总额超过 1000 的用户
SELECT user_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY user_id
HAVING SUM(total_amount) > 1000;
-- 查询员工数量超过 10 的部门
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department
HAVING COUNT(*) > 10;
-- WHERE 和 HAVING 组合使用
SELECT department, AVG(salary) AS avg_salary
FROM employees
WHERE hire_date > DATE '2020-01-01' -- 分组前筛选:只统计 2020 年后入职的员工
GROUP BY department
HAVING AVG(salary) > 50000; -- 分组后筛选:平均工资超过 50000 的部门
ORDER BY 排序
ORDER BY 用于对查询结果排序。
sql
-- 升序排序(默认)
SELECT * FROM products ORDER BY price ASC;
SELECT * FROM products ORDER BY price; -- ASC 可以省略
-- 降序排序
SELECT * FROM products ORDER BY price DESC;
-- 多列排序
SELECT * FROM employees
ORDER BY department ASC, salary DESC; -- 先按部门升序,再按工资降序
-- 使用列位置排序(不推荐,可读性差)
SELECT name, price FROM products ORDER BY 2 DESC; -- 按第 2 列(price)降序
-- NULL 值排序
SELECT * FROM users ORDER BY phone NULLS FIRST; -- NULL 值排在前面
SELECT * FROM users ORDER BY phone NULLS LAST; -- NULL 值排在后面
LIMIT 和 OFFSET
LIMIT 用于限制返回的行数,OFFSET 用于跳过指定数量的行,常用于分页。
sql
-- 限制返回行数
SELECT * FROM products LIMIT 10; -- 返回前 10 条
-- 分页查询
SELECT * FROM products LIMIT 10 OFFSET 0; -- 第 1 页(第 1-10 条)
SELECT * FROM products LIMIT 10 OFFSET 10; -- 第 2 页(第 11-20 条)
SELECT * FROM products LIMIT 10 OFFSET 20; -- 第 3 页(第 21-30 条)
-- 计算 OFFSET
-- 第 n 页(每页 page_size 条):OFFSET = (n - 1) * page_size
注意: LIMIT 和 OFFSET 不是 SQL 标准的一部分,但被大多数数据库支持。标准的分页方式是使用 FETCH 和 OFFSET:
sql
-- SQL 标准分页语法
SELECT * FROM products
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
子查询
子查询 (Subquery) 是嵌套在另一个查询中的查询。
WHERE 子句中的子查询:
sql
-- 查询价格高于平均价格的商品
SELECT * FROM products
WHERE price > (SELECT AVG(price) FROM products);
-- 查询有订单的用户
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders);
-- 查询没有订单的用户
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM orders WHERE user_id IS NOT NULL);
FROM 子句中的子查询:
sql
-- 查询每个部门的平均工资
SELECT dept_avg.department, dept_avg.avg_salary
FROM (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) AS dept_avg
WHERE dept_avg.avg_salary > 60000;
SELECT 子句中的子查询:
sql
-- 显示每个用户及其订单数量
SELECT
u.username,
(SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
FROM users u;
CTE(公共表表达式)
CTE (Common Table Expression) 使用 WITH 关键字定义临时结果集,提高查询的可读性和可维护性。
sql
-- 单个 CTE
WITH high_value_orders AS (
SELECT user_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY user_id
HAVING SUM(total_amount) > 1000
)
SELECT u.username, hvo.total_spent
FROM users u
JOIN high_value_orders hvo ON u.id = hvo.user_id;
-- 多个 CTE
WITH
user_order_count AS (
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
),
user_total_spent AS (
SELECT user_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY user_id
)
SELECT
u.username,
COALESCE(uoc.order_count, 0) AS order_count,
COALESCE(uts.total_spent, 0) AS total_spent
FROM users u
LEFT JOIN user_order_count uoc ON u.id = uoc.user_id
LEFT JOIN user_total_spent uts ON u.id = uts.user_id;
-- 递归 CTE(树形结构遍历)
WITH RECURSIVE category_tree AS (
-- 基础查询:顶层分类
SELECT id, name, parent_id, 1 AS level
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- 递归查询:子分类
SELECT c.id, c.name, c.parent_id, ct.level + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY level, id;
完整查询示例
以下是一个综合使用多种查询技巧的复杂示例:
sql
-- 查询 2024 年每个月的销售统计,包括订单数量、总销售额、平均订单金额
-- 只统计已支付的订单,按月份降序排列
WITH monthly_sales AS (
SELECT
EXTRACT(YEAR FROM order_date) AS year,
EXTRACT(MONTH FROM order_date) AS month,
COUNT(*) AS order_count,
SUM(total_amount) AS total_sales,
AVG(total_amount) AS avg_order_amount
FROM orders
WHERE status = 'paid'
AND order_date >= DATE '2024-01-01'
AND order_date < DATE '2025-01-01'
GROUP BY EXTRACT(YEAR FROM order_date), EXTRACT(MONTH FROM order_date)
)
SELECT
year,
month,
order_count,
ROUND(total_sales, 2) AS total_sales,
ROUND(avg_order_amount, 2) AS avg_order_amount,
-- 计算环比增长率(与上月对比)
ROUND(
(total_sales - LAG(total_sales) OVER (ORDER BY year, month)) * 100.0
/ NULLIF(LAG(total_sales) OVER (ORDER BY year, month), 0),
2
) AS growth_rate_pct
FROM monthly_sales
ORDER BY year DESC, month DESC;
这个示例展示了:
- CTE 简化复杂查询
- 日期函数
EXTRACT提取年月 - 聚合函数
COUNT、SUM、AVG WHERE子句条件筛选GROUP BY分组统计ROUND函数格式化数字- 窗口函数
LAG计算环比 ORDER BY排序结果
数据操作语言 (DML)
DML (Data Manipulation Language) 用于操作表中的数据,包括插入、更新和删除。DML 操作会直接修改数据内容,通常在事务中执行以保证数据一致性。
INSERT 语句
INSERT 用于向表中插入新数据。
插入单行:
sql
-- 指定所有列的值
INSERT INTO users (id, username, email, created_at)
VALUES (1, 'alice', 'alice@example.com', CURRENT_TIMESTAMP);
-- 如果列顺序与表定义一致,可以省略列名
INSERT INTO users
VALUES (2, 'bob', 'bob@example.com', CURRENT_TIMESTAMP);
-- 省略有默认值或允许 NULL 的列
INSERT INTO users (id, username, email)
VALUES (3, 'charlie', 'charlie@example.com');
插入多行:
sql
INSERT INTO products (id, name, price, stock_quantity)
VALUES
(1, 'Laptop', 999.99, 10),
(2, 'Mouse', 19.99, 50),
(3, 'Keyboard', 49.99, 30);
从查询结果插入:
sql
-- 将查询结果插入另一个表
INSERT INTO archived_orders (id, user_id, total_amount, order_date)
SELECT id, user_id, total_amount, order_date
FROM orders
WHERE order_date < DATE '2024-01-01';
-- 创建表并插入数据
CREATE TABLE high_value_customers AS
SELECT user_id, SUM(total_amount) AS total_spent
FROM orders
GROUP BY user_id
HAVING SUM(total_amount) > 10000;
返回插入的数据:
sql
-- 使用 RETURNING 子句返回插入的行(PostgreSQL 等支持)
INSERT INTO users (username, email)
VALUES ('david', 'david@example.com')
RETURNING id, username, created_at;
UPDATE 语句
UPDATE 用于修改表中已存在的数据。
更新所有行:
sql
-- 给所有商品涨价 10%
UPDATE products
SET price = price * 1.1;
条件更新:
sql
-- 更新指定用户的邮箱
UPDATE users
SET email = 'newemail@example.com'
WHERE id = 1;
-- 更新多列
UPDATE users
SET email = 'newemail@example.com', updated_at = CURRENT_TIMESTAMP
WHERE id = 1;
-- 使用表达式更新
UPDATE products
SET stock_quantity = stock_quantity - 5
WHERE id = 10;
基于其他表更新:
sql
-- 根据订单明细更新商品库存
UPDATE products
SET stock_quantity = stock_quantity - (
SELECT SUM(quantity)
FROM order_items
WHERE order_items.product_id = products.id
AND order_items.order_id = 100
)
WHERE id IN (SELECT product_id FROM order_items WHERE order_id = 100);
-- 使用 FROM 子句(PostgreSQL 等支持)
UPDATE products
SET stock_quantity = stock_quantity - oi.quantity
FROM order_items oi
WHERE products.id = oi.product_id
AND oi.order_id = 100;
返回更新的数据:
sql
-- 使用 RETURNING 子句返回更新后的行
UPDATE users
SET is_active = FALSE
WHERE last_login < DATE '2024-01-01'
RETURNING id, username, last_login;
DELETE 语句
DELETE 用于删除表中的数据。
删除所有行:
sql
-- 删除表中所有数据(保留表结构)
DELETE FROM temp_table;
条件删除:
sql
-- 删除指定用户
DELETE FROM users WHERE id = 1;
-- 删除多条记录
DELETE FROM orders WHERE status = 'cancelled';
-- 使用子查询删除
DELETE FROM order_items
WHERE order_id IN (
SELECT id FROM orders WHERE status = 'cancelled'
);
基于其他表删除:
sql
-- 使用 EXISTS 删除
DELETE FROM users
WHERE NOT EXISTS (
SELECT 1 FROM orders WHERE orders.user_id = users.id
);
-- 使用 JOIN 删除(部分数据库支持)
DELETE users
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE orders.id IS NULL;
返回删除的数据:
sql
-- 使用 RETURNING 子句返回删除的行
DELETE FROM users
WHERE is_active = FALSE
RETURNING id, username, email;
DELETE 与 TRUNCATE 的区别:
| 特性 | DELETE | TRUNCATE |
|---|---|---|
| 类型 | DML | DDL |
| 速度 | 慢(逐行删除) | 快(直接释放空间) |
| WHERE 子句 | 支持 | 不支持 |
| 事务回滚 | 可以回滚 | 部分数据库可以回滚 |
| 触发器 | 触发 DELETE 触发器 | 不触发触发器 |
| 自增计数器 | 不重置 | 重置为初始值 |
| 日志记录 | 记录每行删除 | 最小日志记录 |
MERGE 语句
MERGE 语句(也称为 UPSERT)用于根据条件合并插入或更新数据。如果记录存在则更新,不存在则插入。
标准 MERGE 语法:
sql
MERGE INTO products AS target
USING (VALUES
(1, 'Laptop', 1099.99, 15),
(2, 'Mouse', 24.99, 60),
(4, 'Monitor', 299.99, 20)
) AS source (id, name, price, stock_quantity)
ON target.id = source.id
WHEN MATCHED THEN
UPDATE SET
name = source.name,
price = source.price,
stock_quantity = source.stock_quantity
WHEN NOT MATCHED THEN
INSERT (id, name, price, stock_quantity)
VALUES (source.id, source.name, source.price, source.stock_quantity);
PostgreSQL 的 ON CONFLICT 语法:
sql
-- 插入或更新(根据主键或唯一约束)
INSERT INTO products (id, name, price, stock_quantity)
VALUES (1, 'Laptop', 1099.99, 15)
ON CONFLICT (id)
DO UPDATE SET
name = EXCLUDED.name,
price = EXCLUDED.price,
stock_quantity = EXCLUDED.stock_quantity;
-- 插入或忽略
INSERT INTO products (id, name, price, stock_quantity)
VALUES (1, 'Laptop', 1099.99, 15)
ON CONFLICT (id)
DO NOTHING;
-- 批量 UPSERT
INSERT INTO products (id, name, price, stock_quantity)
VALUES
(1, 'Laptop', 1099.99, 15),
(2, 'Mouse', 24.99, 60),
(4, 'Monitor', 299.99, 20)
ON CONFLICT (id)
DO UPDATE SET
price = EXCLUDED.price,
stock_quantity = EXCLUDED.stock_quantity;
MySQL 的 ON DUPLICATE KEY UPDATE 语法:
sql
INSERT INTO products (id, name, price, stock_quantity)
VALUES (1, 'Laptop', 1099.99, 15)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
price = VALUES(price),
stock_quantity = VALUES(stock_quantity);
事务控制
事务 (Transaction) 是一组 SQL 语句的逻辑执行单元,要么全部成功,要么全部失败。事务确保数据的一致性和完整性。
事务的 ACID 特性:
- 原子性 (Atomicity):事务中的所有操作要么全部完成,要么全部不完成
- 一致性 (Consistency):事务执行前后,数据库从一个一致性状态转换到另一个一致性状态
- 隔离性 (Isolation):多个事务并发执行时,互不干扰
- 持久性 (Durability):事务提交后,对数据的修改是永久的
基本事务控制:
sql
-- 开始事务
BEGIN; -- 或 START TRANSACTION;
-- 执行 SQL 操作
INSERT INTO accounts (id, user_id, balance) VALUES (1, 100, 1000.00);
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 提交事务(使更改永久生效)
COMMIT;
sql
-- 回滚事务(撤销所有更改)
BEGIN;
INSERT INTO orders (user_id, total_amount) VALUES (1, 500.00);
-- 发现错误,回滚
ROLLBACK;
保存点 (Savepoint):
sql
BEGIN;
INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');
-- 设置保存点
SAVEPOINT sp1;
INSERT INTO users (username, email) VALUES ('bob', 'bob@example.com');
-- 回滚到保存点(撤销 bob 的插入,但保留 alice)
ROLLBACK TO SAVEPOINT sp1;
-- 提交事务
COMMIT;
事务隔离级别:
SQL 标准定义了四种事务隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
sql
-- 设置事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- 执行事务操作
COMMIT;
事务使用示例:
sql
-- 银行转账示例
BEGIN;
-- 检查余额是否充足
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE; -- 锁定行
-- 扣款
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 到账
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 记录转账记录
INSERT INTO transactions (from_account, to_account, amount, transaction_date)
VALUES (1, 2, 100, CURRENT_TIMESTAMP);
-- 提交事务
COMMIT;
数据控制语言 (DCL)
DCL (Data Control Language) 用于控制数据库的访问权限。通过 DCL,数据库管理员可以管理用户、角色和权限,确保数据的安全性。
用户管理
用户 (User) 是数据库的访问主体,每个用户都有独立的身份和权限。
创建用户:
sql
-- 创建用户并设置密码
CREATE USER alice WITH PASSWORD 'secure_password';
-- 创建用户时指定更多属性
CREATE USER bob WITH
PASSWORD 'password123'
CREATEDB -- 允许创建数据库
VALID UNTIL '2025-12-31'; -- 账户有效期
修改用户:
sql
-- 修改用户密码
ALTER USER alice WITH PASSWORD 'new_password';
-- 修改用户属性
ALTER USER bob WITH NOCREATEDB; -- 撤销创建数据库权限
-- 重命名用户
ALTER USER alice RENAME TO alice_admin;
删除用户:
sql
-- 删除用户
DROP USER bob;
-- 如果存在才删除
DROP USER IF EXISTS bob;
角色管理
角色 (Role) 是一组权限的集合。角色与用户的区别在于:
- 用户 (User):可以登录数据库的实体,具有身份验证信息(如密码)
- 角色 (Role):权限的集合,不一定能登录,主要用于权限管理
角色的优势:
- 简化权限管理:将相同权限的用户归为一组
- 易于维护:修改角色权限会自动影响所有拥有该角色的用户
- 支持权限继承:角色可以授予其他角色
创建角色:
sql
-- 创建角色
CREATE ROLE readonly;
CREATE ROLE admin;
-- 创建可登录的角色(实际上就是用户)
CREATE ROLE developer WITH LOGIN PASSWORD 'dev123';
授予角色给用户:
sql
-- 将角色授予用户
GRANT readonly TO alice;
GRANT admin TO bob;
-- 将角色授予另一个角色(角色继承)
GRANT readonly TO developer;
撤销角色:
sql
-- 从用户撤销角色
REVOKE readonly FROM alice;
删除角色:
sql
-- 删除角色
DROP ROLE readonly;
-- 如果存在才删除
DROP ROLE IF EXISTS readonly;
GRANT 语句
GRANT 用于授予用户或角色特定的数据库权限。
授予表权限:
sql
-- 授予单个权限
GRANT SELECT ON users TO alice;
-- 授予多个权限
GRANT SELECT, INSERT, UPDATE ON products TO bob;
-- 授予所有权限
GRANT ALL PRIVILEGES ON orders TO admin;
-- 授予特定列的权限
GRANT SELECT (id, username), UPDATE (email) ON users TO alice;
授予数据库权限:
sql
-- 授予连接数据库的权限
GRANT CONNECT ON DATABASE company TO alice;
-- 授予创建 schema 的权限
GRANT CREATE ON DATABASE company TO alice;
授予 schema 权限:
sql
-- 授予 schema 的使用权限
GRANT USAGE ON SCHEMA public TO alice;
-- 授予在 schema 中创建对象的权限
GRANT CREATE ON SCHEMA public TO alice;
授予所有表的权限:
sql
-- 授予 schema 中所有表的权限
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- 授予未来创建的表的权限(PostgreSQL)
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly;
授予执行权限:
sql
-- 授予执行函数或存储过程的权限
GRANT EXECUTE ON FUNCTION calculate_total(INTEGER) TO alice;
WITH GRANT OPTION:
sql
-- 授予权限,并允许被授予者将权限授予其他人
GRANT SELECT ON users TO alice WITH GRANT OPTION;
-- alice 现在可以将 SELECT 权限授予其他用户
GRANT SELECT ON users TO bob; -- alice 执行
REVOKE 语句
REVOKE 用于撤销用户或角色的权限。
撤销表权限:
sql
-- 撤销单个权限
REVOKE SELECT ON users FROM alice;
-- 撤销多个权限
REVOKE INSERT, UPDATE ON products FROM bob;
-- 撤销所有权限
REVOKE ALL PRIVILEGES ON orders FROM alice;
撤销授予权限的能力:
sql
-- 撤销 GRANT OPTION
REVOKE GRANT OPTION FOR SELECT ON users FROM alice;
级联撤销:
sql
-- 撤销权限,并级联撤销由该用户授予他人的权限
REVOKE SELECT ON users FROM alice CASCADE;
-- 只撤销直接授予的权限,不影响他人
REVOKE SELECT ON users FROM alice RESTRICT;
权限类型
SQL 标准定义了以下主要权限类型:
| 权限 | 说明 | 适用对象 |
|---|---|---|
SELECT |
查询数据 | 表、视图、列 |
INSERT |
插入数据 | 表、列 |
UPDATE |
更新数据 | 表、列 |
DELETE |
删除数据 | 表 |
TRUNCATE |
清空表数据 | 表 |
REFERENCES |
创建外键引用 | 表、列 |
TRIGGER |
创建触发器 | 表 |
CREATE |
创建对象 | 数据库、Schema |
CONNECT |
连接数据库 | 数据库 |
EXECUTE |
执行函数/存储过程 | 函数、存储过程 |
USAGE |
使用 Schema 或其他对象 | Schema、序列 |
ALL PRIVILEGES |
所有权限 | 所有对象 |
权限管理示例
以下是一个完整的权限管理场景:
场景:电商系统的权限管理
sql
-- 1. 创建角色
CREATE ROLE readonly; -- 只读角色
CREATE ROLE data_entry; -- 数据录入角色
CREATE ROLE analyst; -- 数据分析角色
CREATE ROLE admin; -- 管理员角色
-- 2. 为只读角色授予权限
GRANT CONNECT ON DATABASE ecommerce TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- 3. 为数据录入角色授予权限
GRANT CONNECT ON DATABASE ecommerce TO data_entry;
GRANT USAGE ON SCHEMA public TO data_entry;
GRANT SELECT, INSERT, UPDATE ON products TO data_entry;
GRANT SELECT, INSERT, UPDATE ON orders TO data_entry;
GRANT SELECT, INSERT, UPDATE ON order_items TO data_entry;
-- 4. 为数据分析角色授予权限
GRANT readonly TO analyst; -- 继承只读权限
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst;
-- 5. 为管理员角色授予权限
GRANT ALL PRIVILEGES ON DATABASE ecommerce TO admin;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO admin;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO admin;
-- 6. 创建用户并分配角色
CREATE USER alice WITH PASSWORD 'alice123';
CREATE USER bob WITH PASSWORD 'bob123';
CREATE USER charlie WITH PASSWORD 'charlie123';
CREATE USER david WITH PASSWORD 'david123';
-- 7. 授予角色给用户
GRANT readonly TO alice; -- alice 是只读用户
GRANT data_entry TO bob; -- bob 是数据录入员
GRANT analyst TO charlie; -- charlie 是数据分析师
GRANT admin TO david; -- david 是管理员
-- 8. 查看用户权限(PostgreSQL)
SELECT
grantee,
table_schema,
table_name,
privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'alice';
-- 9. 撤销权限示例
REVOKE INSERT ON products FROM data_entry; -- 数据录入员不能再插入产品
-- 10. 删除用户和角色
REVOKE ALL PRIVILEGES ON DATABASE ecommerce FROM alice;
DROP USER alice;
DROP ROLE readonly;
权限最佳实践:
- 最小权限原则:只授予完成任务所需的最小权限
- 使用角色管理权限:避免直接为用户授权,使用角色统一管理
- 定期审计权限:定期检查和清理不必要的权限
- 敏感数据列级权限:对敏感字段(如密码、信用卡号)使用列级权限控制
- 分离职责:管理员、开发者、分析师使用不同的账户和权限
- 使用视图限制访问:创建视图隐藏敏感列或行,授予用户视图的访问权限
sql
-- 示例:创建视图隐藏敏感信息
CREATE VIEW users_public AS
SELECT id, username, email, created_at
FROM users; -- 隐藏 password_hash 列
-- 授予访问视图的权限
GRANT SELECT ON users_public TO readonly;
本文系统介绍了 SQL 标准语法的四大核心类别:DDL、DQL、DML 和 DCL。掌握这些基础语法是使用 SQL 进行数据库操作的基础。在实际应用中,应根据具体数据库的实现细节调整语法,同时遵循最佳实践确保代码的可维护性和数据的安全性。