一、SQL基本数据类型
SQL 数据类型速查表
类别 | 数据类型 | 说明 | 范围/示例 | 适用场景 |
---|---|---|---|---|
整数类型 | INT / INTEGER | 标准整数类型 | -2³¹ 到 2³¹-1 (-2,147,483,648 到 2,147,483,647) | ID、年龄、数量等 |
SMALLINT | 小范围整数 | -32,768 到 32,767 | 小范围数值 | |
BIGINT | 大范围整数 | -2⁶³ 到 2⁶³-1 | 大额订单号、时间戳 | |
TINYINT | 极小整数 (MySQL) | 0 到 255 或 -128 到 127 | 状态码、布尔值模拟 | |
小数类型 | DECIMAL(p,s) | 精确小数 p=总位数, s=小数位 | DECIMAL(10,2) → 12345678.99 | 金融金额、精确计算 |
FLOAT | 单精度浮点数 | ≈6-7位精度 | 科学计算 | |
DOUBLE | 双精度浮点数 | ≈15位精度 | 高精度测量 | |
字符串类型 | CHAR(n) | 定长字符串 空格填充 | CHAR(10) ‘ABC’ → 'ABC ’ | 固定长度代码(如国家代码) |
VARCHAR(n) | 变长字符串 | VARCHAR(255) | 姓名、地址等变长文本 | |
TEXT | 大文本数据 | 最多 65,535 字节 (MySQL) | 文章内容、描述 | |
日期时间 | DATE | 日期 | ‘2023-08-15’ | 出生日期、事件日期 |
TIME | 时间 | ‘14:30:00’ | 会议时间 | |
DATETIME | 日期+时间 | ‘2023-08-15 14:30:00’ | 订单时间、日志时间戳 | |
TIMESTAMP | 自动记录的时间戳 | 自动记录修改时间 | 创建时间/修改时间 | |
二进制类型 | BLOB | 二进制大对象 | 最大 65,535 字节 | 图片、PDF等文件存储 |
BINARY(n) | 定长二进制数据 | BINARY(16) | 加密数据、哈希值 | |
布尔类型 | BOOLEAN | 逻辑值 | TRUE/FALSE | 开关状态、是否标记 |
特殊类型 | ENUM('val1','val2') | 枚举值 | ENUM(‘Red’,‘Green’,‘Blue’) | 状态选项、固定分类 |
JSON | JSON格式数据 (现代数据库) | {“name”: “John”, “age”: 30} | 灵活数据结构 | |
UUID | 全局唯一标识符 (PostgreSQL等) | ‘a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11’ | 分布式ID生成 |
二、SQL关键字解释
SQL查询知识点
基本查询结构
SELECT [DISTINCT] 列1, 列2, 聚合函数(列3) -- 选择最终显示的列
FROM 表1 -- 初始数据源
[JOIN 表2 ON 连接条件] -- 表连接
WHERE 行级过滤条件 -- 行过滤
GROUP BY 分组列 -- 数据分组
HAVING 组级过滤条件 -- 组过滤
ORDER BY 排序列 [ASC|DESC] -- 结果排序
LIMIT 结果数量; -- 行数限制
关键字详解(含执行变化案例)
1. SELECT
-
作用:指定最终结果集中显示的列
-
执行变化:从中间结果集中抽取指定列
-
示例:
/* 原始products表(执行前): | id | product_name | price | |----|--------------|-------| | 1 | Laptop | 1200 | | 2 | Phone | 800 | | 3 | Tablet | 600 | */ SELECT product_name, price * 0.9 AS sale_price FROM products;/* 执行后结果集: | product_name | sale_price | |--------------|------------| | Laptop | 1080 | | Phone | 720 | | Tablet | 540 | */
2. FROM
- 作用:指定查询的主数据源
- 执行变化:加载初始数据集
- *示例:
FROM employees -- 加载员工表全部数据
3. JOIN(表连接)
- 作用:组合多个表的数据
- 执行变化:扩展列维度
INNER JOIN 示例:
/* employees表(左表):
| id | name | dept |
|----|-------|--------|
| 1 | Alice | Sales |
| 2 | Bob | IT |orders表(右表):
| order_id | emp_id | amount |
|----------|--------|--------|
| 101 | 1 | 200 |
| 102 | 1 | 300 | */SELECT e.name, o.amount
FROM employees e
INNER JOIN orders o ON e.id = o.emp_id;/* 执行后结果集:
| name | amount |
|-------|--------|
| Alice | 200 |
| Alice | 300 | */
4. WHERE
-
作用:行级数据过滤
-
执行变化:减少行数
-
示例:
/* 原始orders表: | id | amount | status | |----|--------|----------| | 1 | 100 | shipped | | 2 | 200 | pending | | 3 | 150 | shipped | */ SELECT id, amount FROM orders WHERE status = 'shipped';/* 执行后结果集: | id | amount | |----|--------| | 1 | 100 | | 3 | 150 | */
5. GROUP BY
- 作用:数据分组聚合
- 执行变化:行数减少,出现聚合值
-
执行变化:行数减少,出现聚合值
-
聚合值
示例:
/* 原始employees表:
| id | name | dept | salary |
|----|--------|--------|--------|
| 1 | Alice | Sales | 5000 |
| 2 | Bob | Sales | 6000 |
| 3 | Charlie| IT | 7000 | */SELECT dept, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept;/* 执行后结果集:
| dept | avg_salary |
|-------|------------|
| Sales | 5500 |
| IT | 7000 | */
6. HAVING
- 作用:分组后结果过滤
- 执行变化:减少分组数量
- 与WHERE对比:
特性 | WHERE | HAVING |
---|---|---|
执行时机 | 分组前 | 分组后 |
操作对象 | 原始行 | 分组结果 |
聚合函数 | 不可用 | 可用 |
示例:
/* 续上GROUP BY结果:
| dept | avg_salary |
|-------|------------|
| Sales | 5500 |
| IT | 7000 | */SELECT dept, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept
HAVING AVG(salary) > 6000;/* 执行后结果集:
| dept | avg_salary |
|------|------------|
| IT | 7000 | */
7. ORDER BY
-
作用:结果排序
-
执行变化:行顺序改变,内容不变
-
排序方式:
ASC
升序(默认)DESC
降序
-
示例:
/* 原始结果: | product | price | |---------|-------| | Laptop | 1200 | | Phone | 800 | | Tablet | 600 | */ SELECT product, price FROM products ORDER BY price DESC;/* 执行后结果集: | product | price | |---------|-------| | Laptop | 1200 | | Phone | 800 | | Tablet | 600 | */
8. LIMIT
-
作用:限制返回行数
-
执行变化:截断结果集
-
示例:
/* 假设排序后结果(10条记录) */ SELECT product, price FROM products ORDER BY price DESC LIMIT 5; /* 执行后结果集:只保留前5条记录 */
9. OFFSET
OFFSET
通常与 LIMIT
配合使用,用于实现分页查询或跳过指定行数
-
跳过结果集的前 N 行
-
典型应用场景:分页查询(如第2页、第3页数据)
-
语法结构:
LIMIT 返回行数 OFFSET 跳过行数; -- 或等效写法 -- LIMIT 跳过行数, 返回行数; # MySQL专用语法
🧩 执行机制图解
📊 案例演示(分步说明)
初始数据表 products
id | product_name | price |
---|---|---|
1 | Laptop | 1200 |
2 | Phone | 800 |
3 | Tablet | 600 |
4 | Monitor | 300 |
5 | Keyboard | 50 |
6 | Mouse | 30 |
7 | Headphones | 150 |
查询1:基础分页(每页3条)
/* 获取第1页数据 */
SELECT id, product_name
FROM products
ORDER BY price DESC
LIMIT 3 OFFSET 0; -- 从第0行开始取3条/* 结果集:
| id | product_name |
|----|--------------|
| 1 | Laptop |
| 2 | Phone |
| 3 | Tablet | */
查询2:跳转到第2页
/* 获取第2页数据 */
SELECT id, product_name
FROM products
ORDER BY price DESC
LIMIT 3 OFFSET 3; -- 跳过前3条,取接下来3条/* 结果集:
| id | product_name |
|----|--------------|
| 4 | Monitor |
| 7 | Headphones |
| 5 | Keyboard | */
查询3:等效写法(MySQL)
/* 获取第3页数据 */
SELECT id, product_name
FROM products
ORDER BY price DESC
LIMIT 6, 3; -- 等效于 OFFSET 6 LIMIT 3/* 结果集:
| id | product_name |
|----|--------------|
| 6 | Mouse | # 只剩1条数据 */
⚠️ 重要注意事项
- 执行顺序:
OFFSET
在ORDER BY
之后、LIMIT
之前执行
10.字符串操作与通配符
通配符 | 功能 | 示例 | 匹配示例 |
---|---|---|---|
% | 匹配任意长度字符 | 'John%' | John, Johnson |
_ | 匹配单个字符 | '_ean' | Dean, Jean |
[ ] | 匹配指定字符集 | '[a]%' (SQL Server) | apple, elephant |
[^] | 排除指定字符集 | '[^0-9]%' | Apple, #123 |
🧰 核心字符串函数
1. 基础操作函数
函数 | 功能 | 示例 | 结果 |
---|---|---|---|
CONCAT(s1, s2) | 字符串拼接 | CONCAT('Hello', ' ', 'World') | Hello World |
LENGTH(s) | 字符串长度 | LENGTH('SQL') | 3 |
UPPER(s) | 转为大写 | UPPER('hello') | HELLO |
LOWER(s) | 转为小写 | LOWER('SQL') | sql |
TRIM(s) | 去除两端空格 | TRIM(' text ') | text |
2. 子字符串操作
函数 | 功能 | 示例 | 结果 |
---|---|---|---|
SUBSTRING(s, start, len) | 提取子字符串 | SUBSTRING('Database', 2, 4) | atab |
LEFT(s, n) | 提取左侧n个字符 | LEFT('2023-06-15', 4) | 2023 |
RIGHT(s, n) | 提取右侧n个字符 | RIGHT('user@email.com', 3) | com |
REPLACE(s, old, new) | 替换字符串 | REPLACE('I like SQL', 'like', 'love') | I love SQL |
REVERSE(s) | 反转字符串 | REVERSE('ABCD') | DCBA |
3. 高级搜索函数
函数 | 功能 | 示例 |
---|---|---|
POSITION(sub IN s) | 返回子串位置 | POSITION('@' IN 'user@domain.com') → 5 |
CHAR_LENGTH(s) | 字符数(支持多字节) | CHAR_LENGTH('中文') → 2 |
INSTR(s, sub) | 查找子串位置 | INSTR('SQL Tutorial', 'Tut') → 5 |
⚙️ 实战综合应用
场景:邮箱格式标准化
UPDATE users
SET email = LOWER(CONCAT(SUBSTRING(email, 1, POSITION('@' IN email) - 1), -- 用户名部分'@company.com' -- 统一域名)
)
WHERE email NOT LIKE '%@company.com'; -- 筛选需要修改的记录
场景:产品编号验证
/* 验证格式:AA-000 */
SELECT product_code
FROM products
WHERE product_code LIKE '__-___' -- 长度验证AND SUBSTRING(product_code, 1, 2) REGEXP '^[A-Z]{2}$' -- 前两位大写字母AND SUBSTRING(product_code, 4, 3) REGEXP '^[0-9]{3}$'; -- 后三位数字
场景:动态搜索
/* 根据输入关键词灵活搜索 */
SET @keyword = 'pro yoga';SELECT * FROM products
WHERE product_name LIKE CONCAT('%', REPLACE(@keyword, ' ', '%'), '%')OR category LIKE CONCAT('%', @keyword, '%');
完整案例:销售分析查询
业务场景:分析2023年各部门销售业绩
初始数据
employees表:
id | name | dept |
---|---|---|
1 | Alice | Sales |
2 | Bob | Sales |
3 | Charlie | IT |
orders表:
order_id | emp_id | amount | order_date |
---|---|---|---|
101 | 1 | 200 | 2023-02-01 |
102 | 1 | 300 | 2023-03-15 |
103 | 2 | 150 | 2023-01-10 |
104 | 3 | 400 | 2023-04-20 |
105 | 1 | 500 | 2022-12-31 |
分步执行过程
SELECT e.dept,SUM(o.amount) AS total_sales,COUNT(*) AS order_count
FROM employees e
INNER JOIN orders o ON e.id = o.emp_id
WHERE o.order_date >= '2023-01-01'
GROUP BY e.dept
HAVING SUM(o.amount) > 0
ORDER BY total_sales DESC;
分步结果变化:
-
FROM + JOIN(初始连接):
| e.id | e.name | dept | o.order_id | amount | order_date | |------|--------|-------|------------|--------|-------------| | 1 | Alice | Sales | 101 | 200 | 2023-02-01 | | 1 | Alice | Sales | 102 | 300 | 2023-03-15 | | 1 | Alice | Sales | 105 | 500 | 2022-12-31 | | 2 | Bob | Sales | 103 | 150 | 2023-01-10 | | 3 | Charlie| IT | 104 | 400 | 2023-04-20 |
-
WHERE(日期过滤):
| e.id | name | dept | order_id | amount | order_date | |------|--------|-------|----------|--------|-------------| | 1 | Alice | Sales | 101 | 200 | 2023-02-01 | | 1 | Alice | Sales | 102 | 300 | 2023-03-15 | | 2 | Bob | Sales | 103 | 150 | 2023-01-10 | | 3 | Charlie| IT | 104 | 400 | 2023-04-20 |
-
GROUP BY(按部门分组):
Sales组:Alice(200+300) + Bob(150) = 650 IT组:Charlie(400) = 400
-
HAVING(组过滤):
两个组都满足SUM(amount)>0
-
SELECT(显示结果):
| dept | total_sales | order_count | |-------|-------------|-------------| | Sales | 650 | 3 | | IT | 400 | 1 |
-
ORDER BY(最终排序):
| dept | total_sales | order_count | |-------|-------------|-------------| | Sales | 650 | 3 | -- 销售额最高 | IT | 400 | 1 |
SQL更新
1. 插入数据(INSERT)
关键字解释
INSERT INTO
:指定要插入数据的表名。VALUES
:定义插入的具体值。SET
(可选):在部分数据库(如MySQL)中可用,用于指定列名和值的对应关系。
语法结构
-- 插入单条数据
INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);-- 插入多条数据
INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...),(值3, 值4, ...);
示例
假设有一个 students
表:
id | name | age |
---|---|---|
1 | Alice | 20 |
插入新数据:
INSERT INTO students (name, age) VALUES ('Bob', 22);
2. 修改数据(UPDATE)
关键字解释
UPDATE
:指定要修改数据的表名。SET
:定义要修改的列名及其新值。WHERE
(可选):限定修改的行范围(必须谨慎使用)。
语法结构
UPDATE 表名
SET 列1 = 新值1, 列2 = 新值2
WHERE 条件;
示例
修改 students
表中 id=1
的学生的年龄:
UPDATE students
SET age = 21
WHERE id = 1;
3. 删除数据(DELETE)
关键字解释
DELETE FROM
:指定要删除数据的表名。WHERE
(可选):限定删除的行范围(必须谨慎使用)。
语法结构
DELETE FROM 表名
WHERE 条件;
示例
删除 students
表中 id=2
的学生:
DELETE FROM students
WHERE id = 2;
4. 综合示例
场景
修改订单表中某个用户的订单状态,并删除过期订单,最后插入新订单。
SQL 操作
-- 修改订单状态
UPDATE orders
SET status = '已完成'
WHERE user_id = 1001 AND status = '处理中';-- 删除过期订单
DELETE FROM orders
WHERE order_date < '2023-01-01';-- 插入新订单
INSERT INTO orders (user_id, product, status)
VALUES (1002, 'Laptop', '已下单');
三、创建,修改,删除表
🏗️ 一、创建表 (CREATE TABLE)
📌 核心语法
CREATE TABLE [IF NOT EXISTS] 表名 (列名1 数据类型 [约束],列名2 数据类型 [约束],...[表级约束]
);
🛡️ 常用约束
约束类型 | 关键字 | 核心作用 | 示例 | 可视化说明 |
---|---|---|---|---|
主键约束 | PRIMARY KEY | 唯一标识每行记录,禁止重复和 NULL | id INT PRIMARY KEY | 🔑 唯一标识符 |
外键约束 | FOREIGN KEY | 强制表间引用完整性 | user_id INT REFERENCES users(id) ON DELETE CASCADE | ⛓️ 表间连接关系 |
唯一约束 | UNIQUE | 确保列值唯一(允许多个 NULL) | email VARCHAR(255) UNIQUE | ✨ 禁止重复值 |
非空约束 | NOT NULL | 禁止 NULL 值 | name VARCHAR(50) NOT NULL | 🚫 强制必填项 |
检查约束 | CHECK | 强制自定义条件 | age INT CHECK (age >= 18) | ✅ 数据验证规则 |
默认值约束 | DEFAULT | 未指定值时自动填充默认值 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | ⏱️ 自动填充值 |
自动增长约束 | AUTO_INCREMENT | 自动生成连续唯一值(MySQL) | id INT AUTO_INCREMENT PRIMARY KEY | 🔢 自增序列 |
枚举约束 | ENUM | 限制列值为预定义选项 | status ENUM('active','inactive') | 📋 固定选项列表 |
1. 主键约束 (PRIMARY KEY)
作用:唯一标识表的每行记录
特性:
- 值必须唯一且非 NULL
- 每表只能有一个主键(可多列组合)
案例:
CREATE TABLE students (student_id INT PRIMARY KEY, -- 单列主键name VARCHAR(50) NOT NULL
);-- 多列组合主键
CREATE TABLE order_items (order_id INT,product_id INT,quantity INT,PRIMARY KEY (order_id, product_id) -- 复合主键
);
2. 外键约束 (FOREIGN KEY)
作用:维护表间数据一致性
关键参数:
ON DELETE CASCADE
:主表删除时同步删除关联记录ON UPDATE SET NULL
:主表更新时关联字段置空
案例:
CREATE TABLE departments (dept_id INT PRIMARY KEY,dept_name VARCHAR(50)
);CREATE TABLE employees (emp_id INT PRIMARY KEY,name VARCHAR(50),dept_id INT,FOREIGN KEY (dept_id) REFERENCES departments(dept_id) ON DELETE SET NULL -- 部门删除时员工部门置空
);
3. 唯一约束 (UNIQUE)
作用:保证列值唯一性
特性:
- 允许 NULL 值(多个 NULL 视为不同值)
- 每表可创建多个唯一约束
案例:
CREATE TABLE users (user_id INT PRIMARY KEY,username VARCHAR(30) UNIQUE, -- 用户名唯一email VARCHAR(255) UNIQUE, -- 邮箱唯一phone VARCHAR(20) UNIQUE -- 手机号唯一
);
4. 检查约束 (CHECK)
作用:强制数据满足业务规则
支持条件:比较运算、逻辑运算、函数调用
案例:
CREATE TABLE products (product_id INT PRIMARY KEY,name VARCHAR(100),price DECIMAL(10,2) CHECK (price > 0), -- 价格必须>0discount DECIMAL(5,2) CHECK (discount BETWEEN 0 AND 100), -- 折扣0-100%release_date DATE CHECK (release_date > '2020-01-01') -- 2020年后发布
);
5. 默认值约束 (DEFAULT)
作用:自动填充缺省值
常用默认值:
CURRENT_TIMESTAMP
:当前时间戳0
/''
:数值/字符串默认值TRUE
/FALSE
:布尔默认值
案例:
CREATE TABLE orders (order_id INT PRIMARY KEY,order_date DATE DEFAULT CURRENT_DATE, -- 默认当天日期status VARCHAR(20) DEFAULT 'pending', -- 默认状态total DECIMAL(10,2) DEFAULT 0.00 -- 默认总价
);
⚙️ 约束管理操作
添加约束(建表后)
-- 添加主键
ALTER TABLE students ADD PRIMARY KEY (student_id);-- 添加外键
ALTER TABLE employees
ADD CONSTRAINT fk_dept
FOREIGN KEY (dept_id) REFERENCES departments(dept_id);-- 添加检查约束
ALTER TABLE products
ADD CHECK (price > 0);
删除约束
-- 删除主键
ALTER TABLE students DROP PRIMARY KEY;-- 删除外键
ALTER TABLE employees DROP FOREIGN KEY fk_dept;-- 删除唯一约束
ALTER TABLE users DROP INDEX email;
🧩 案例1:创建用户表
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(30) NOT NULL UNIQUE,email VARCHAR(100) NOT NULL,password CHAR(60) NOT NULL, -- 存储加密密码created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,is_active BOOLEAN DEFAULT TRUE
);
🧩 案例2:创建订单表(含外键)
CREATE TABLE orders (order_id INT AUTO_INCREMENT PRIMARY KEY,user_id INT NOT NULL,amount DECIMAL(10,2) CHECK (amount > 0),order_date DATE DEFAULT (CURRENT_DATE),status ENUM('pending', 'shipped', 'delivered') DEFAULT 'pending',FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
🔧 二、修改表 (ALTER TABLE)
📌 核心操作类型
操作 | 语法示例 |
---|---|
添加列 | ALTER TABLE 表名 ADD COLUMN 列名 数据类型 |
删除列 | ALTER TABLE 表名 DROP COLUMN 列名 |
修改列类型 | ALTER TABLE 表名 ALTER COLUMN 列名 新类型 |
重命名列 | ALTER TABLE 表名 RENAME COLUMN 旧名 TO 新名 |
添加约束 | ALTER TABLE 表名 ADD CONSTRAINT 约束内容 |
删除约束 | ALTER TABLE 表名 DROP CONSTRAINT 约束名 |
🧩 案例1:添加新列
/* 在users表中添加手机号字段 */
ALTER TABLE users
ADD COLUMN phone VARCHAR(20) NOT NULL DEFAULT '';/* 可视化变化 */
🧩 案例2:修改列属性
/* 扩展email字段长度并添加唯一约束 */
ALTER TABLE users
MODIFY COLUMN email VARCHAR(150) NOT NULL;ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email);
🧩 案例3:删除列
/* 删除已弃用的is_active列 */
ALTER TABLE users
DROP COLUMN is_active;
🧩 案例4:重命名表
/* 将orders表重命名为purchases */
ALTER TABLE orders RENAME TO purchases;
🗑️ 三、删除表 (DROP TABLE)
📌 核心语法
DROP TABLE [IF EXISTS] 表名 [CASCADE];
⚠️ 注意事项
- 不可逆操作:表结构和数据永久删除
- 依赖处理:
CASCADE
:级联删除依赖对象(视图、外键等)RESTRICT
:默认行为,存在依赖时拒绝删除
- 权限要求:需要DROP权限
🧩 案例1:基本删除
/* 删除临时表 */
DROP TABLE IF EXISTS temp_logs;
🧩 案例2:级联删除
/* 删除用户表及关联订单 */
DROP TABLE users CASCADE;/* 可视化效果 */
💡 最佳实践建议
- 创建表时:
-
优先使用
NOT NULL
约束 -
为关联字段添加外键约束
-
使用
AUTO_INCREMENT
/SERIAL
作为代理主键
-
修改表时:
-
生产环境使用
ALTER TABLE ... ADD COLUMN
而非直接修改列类型 -
大表修改在低峰期进行
/* 安全添加列示例 */ ALTER TABLE large_table ADD COLUMN new_column INT DEFAULT 0, ALGORITHM=INPLACE, LOCK=NONE; -- MySQL优化参数
-
-
删除表时:
-
始终使用
IF EXISTS
防止错误 -
正式环境先重命名再删除(安全回收期)
/* 安全删除流程 */ ALTER TABLE obsolete_table RENAME TO obsolete_table_202308; DROP TABLE obsolete_table_202308; -- 30天后执行
-
重要表删除前执行数据备份
CREATE TABLE users_backup AS SELECT * FROM users; -- 全量备份
四、索引:加速查询的核心技术
🚀 索引核心概念
索引本质:数据库中的高效查找数据结构(类似书籍目录)
核心价值:
- 提升查询速度 10~1000 倍
- 保证数据唯一性(唯一索引)
- 加速表连接(JOIN 操作)
索引工作原理
订单表 orders
初始结构
order_id | user_id | amount | order_date |
---|---|---|---|
1001 | 101 | 200 | 2023-08-01 |
1002 | 102 | 150 | 2023-08-02 |
1003 | 101 | 300 | 2023-08-03 |
1004 | 103 | 400 | 2023-08-04 |
🔍 无索引查询(全表扫描)
SELECT * FROM orders WHERE user_id = 101;
执行过程:
- 逐行扫描所有记录(4行)
- 对每行检查
user_id=101
- 返回匹配行(1001和1003)
扫描行数:4行
🚀 创建索引后查询
CREATE INDEX idx_user ON orders(user_id); -- 创建B树索引
SELECT * FROM orders WHERE user_id = 101;
索引结构(B树简化版)
执行过程:
- 从根节点开始查找
user_id=101
- 定位到分支节点 “100-150”
- 找到叶子节点 “user_id=101”
- 通过指针直接访问订单1001和1003
扫描行数:2行(仅目标数据)
⚖️ 索引效果对比
指标 | 无索引 | 有索引 |
---|---|---|
扫描行数 | 4行 | 2行 |
磁盘I/O | 4次 | 2次 |
查询时间 | 15ms | 3ms |
新增订单代价 | 直接写入 | 更新索引+写入 |
🔥 组合索引深度案例
创建组合索引:
CREATE INDEX idx_user_date ON orders(user_id, order_date);
执行范围查询:
SELECT * FROM orders
WHERE user_id = 101 AND order_date BETWEEN '2023-08-01' AND '2023-08-10';
组合索引结构
查询过程:
- 在索引中定位
user_id=101
- 在该分区内按日期范围查找
- 直接获取订单1001和1003
优势:
- 避免全表扫描
- 避免额外排序(索引已预排序)
📊 真实性能测试数据
订单表(100万记录)查询对比:
查询类型 | 无索引时间 | 索引时间 | 提升倍数 |
---|---|---|---|
等值查询(user_id) | 1200ms | 25ms | 48x |
范围查询(date) | 980ms | 40ms | 24x |
排序(order_id) | 850ms | 30ms | 28x |
📌 索引类型速查表
索引类型 | 关键字 | 适用场景 | 可视化结构 |
---|---|---|---|
B树索引 | INDEX | 等值查询、范围查询(默认索引) | 平衡多叉树 🌳 |
唯一索引 | UNIQUE INDEX | 主键/唯一约束字段 | 带唯一标识的叶子节点 🔑 |
全文索引 | FULLTEXT INDEX | 文本内容搜索(文章、描述) | 倒排索引 📝 |
哈希索引 | HASH INDEX | 内存表精确匹配(不支持范围查询) | 键值对存储 ⚡ |
组合索引 | INDEX(col1,col2) | 多列条件查询 | 多级树结构 🧩 |
空间索引 | SPATIAL INDEX | 地理坐标数据(MySQL) | R树结构 🗺️ |
⚙️ 索引操作语法
1. 创建索引
-- 基本单列索引
CREATE INDEX idx_name ON table_name (column_name);-- 唯一索引
CREATE UNIQUE INDEX uidx_email ON users (email);-- 组合索引(多列)
CREATE INDEX idx_name_dept ON employees (last_name, department);-- 全文索引(MySQL)
CREATE FULLTEXT INDEX idx_content ON articles (content);
2. 删除索引
DROP INDEX idx_name ON table_name;
3. 查看索引
SHOW INDEX FROM table_name;
🔍 索引适用场景
推荐创建索引的列:
-
WHERE 子句频繁使用的列
SELECT * FROM orders WHERE user_id = 100; -- 需为user_id建索引
-
JOIN 连接字段
SELECT * FROM orders o JOIN users u ON o.user_id = u.id; -- user_id和id需索引
-
排序字段(ORDER BY)
SELECT * FROM products ORDER BY price DESC; -- price建索引加速排序
-
分组字段(GROUP BY)
SELECT department, COUNT(*) FROM employees GROUP BY department; -- department建索引
⚖️ 索引优缺点分析
✅ 优点:
- 极大加速 SELECT 查询
- 加速表连接(JOIN)
- 保证数据唯一性(唯一索引)
- 优化排序和分组操作
❌ 缺点:
- 降低写操作速度(INSERT/UPDATE/DELETE)
- 占用额外存储空间
- 维护成本高(需定期优化)
五、视图
1. 视图是什么?
- 本质: 一个虚拟表。
- 构成: 基于一个或多个基础表(或其他视图)的SELECT查询的结果集。
- 存储: 不存储实际数据,只存储定义它的 SQL 查询语句。
- 行为: 当查询视图时,数据库引擎会动态执行其定义的 SELECT 语句,返回当前基础表中的最新数据。
- 类比: 像一个保存好的查询,或者一个窗口,透过它看到的是基础表中数据的特定组合、过滤或聚合。
2. 为什么使用视图?
- 简化复杂查询: 将复杂的 JOIN、子查询、过滤封装在视图中,用户只需查询简单的视图。
- 增强数据安全性:
- 隐藏基础表的敏感列(如薪资、密码)。
- 通过视图仅暴露用户有权访问的行和列(结合权限管理)。
- 提供逻辑数据独立性:
- 应用程序可以基于视图编写,即使基础表结构发生变化(如列名、拆分表),只需修改视图定义,应用程序代码可能无需改动。
- 定制化数据展现: 为不同用户或部门提供不同的数据视角(聚合数据、特定范围数据)。
- 简化权限管理: 对视图授权比直接对多个基础表授权更方便。
3. 创建视图 (CREATE VIEW)
CREATE VIEW view_name [(column_alias1, column_alias2, ...)]
AS
SELECT column1, column2, ...
FROM table_name
[WHERE condition]
[GROUP BY ...]
[HAVING ...]
[JOIN ...];
view_name
: 视图的名称。(column_alias1, ...)
: 可选。为视图的列指定别名。如果省略,列名继承 SELECT 语句中的列名或别名。AS
: 关键字。SELECT ...
: 定义视图内容的查询语句。这是视图的核心。
示例:
CREATE VIEW ActiveCustomers AS
SELECT CustomerID, CompanyName, ContactName, Phone
FROM Customers
WHERE Active = 1;
4. 查询视图 (SELECT)
SELECT * FROM ActiveCustomers; -- 查询整个视图
SELECT ContactName, Phone FROM ActiveCustomers WHERE CompanyName LIKE 'A%'; -- 像查询普通表一样使用视图
5. 视图的核心特点(重点理解)
- 虚拟性: 视图不存储数据本身,只存储查询定义。查询视图时实时计算。
- 动态性: 视图数据随基础表数据实时变化。
- 派生性: 视图数据完全来源于其定义所基于的基础表。
- 可更新性 (有时): 并非所有视图都可更新。能否更新取决于视图的定义:
- 通常,基于单个表、不含 DISTINCT、GROUP BY、HAVING、聚合函数,并且包含基础表所有非空且无默认值的列的视图较容易更新。
- 涉及多个表的 JOIN 视图通常不可直接更新(具体规则因数据库系统而异,如 SQL Server 有限支持, Oracle 有 INSTEAD OF 触发器)。
- 更新视图 (INSERT/UPDATE/DELETE) 最终会作用到基础表上。
图表 1:视图与基础表关系
+----------------+ +----------------+ +----------------+
| Table A | | Table B | | Table C |
| (Physical) | | (Physical) | | (Physical) |
| - Col1 (PK) | | - ColX (PK) | | - ColY (PK) |
| - Col2 | | - ColY (FK) | | - ColZ |
| - Col3 | | - ColW | +----------------+
+----------------+ +----------------+^ ^| || JOIN/Filter/Projection ||-------------------------+|v+----------------+| MyView || (Virtual) || - Col1 || - Col2_Alias | <-- (Alias from TableA.Col2)| - ColZ | <-- (From TableC)| - CalcColumn | <-- (e.g., TableA.Col3 * 10)+----------------+^||+----------------+| Application || or User | --> SELECT * FROM MyView WHERE ...+----------------+
- 实线方框: 物理存储的基础表。
- 虚线方框: 虚拟的视图。
- 箭头: 视图的数据来源于执行其定义的 SELECT 语句(包含 JOIN、过滤、计算等操作)作用于基础表。
- 应用/用户: 像查询普通表一样查询视图。
6. 修改视图 (ALTER VIEW / CREATE OR REPLACE VIEW)
-
ALTER VIEW (部分数据库如 SQL Server):
ALTER VIEW view_name [(column_alias1, ...)] AS new_select_statement;
-
CREATE OR REPLACE VIEW (更通用,如 MySQL, PostgreSQL, Oracle):
CREATE OR REPLACE VIEW view_name [(column_alias1, ...)] AS new_select_statement;
7. 删除视图 (DROP VIEW)
DROP VIEW [IF EXISTS] view_name;
8. 视图的优缺点总结
- 优点: 简化、安全、逻辑独立、定制化。
- 缺点:
- 性能开销: 复杂视图查询时,数据库仍需执行底层复杂查询,可能不如直接优化基础表查询快(视图不是性能优化银弹)。
- 更新限制: 并非所有视图都可直接更新,规则有时复杂。
- 依赖管理: 视图依赖于基础表。如果基础表被删除或结构更改(如删除视图中引用的列),视图会失效。
- 嵌套复杂性: 过度使用嵌套视图(视图基于视图)会降低可读性和维护性,并可能放大性能问题。
图表 2:视图的权限控制(概念图)
+----------------+ +----------------+ +----------------+
| User/App | | View | | Base Tables |
| (Limited Perm) |---->| (Permission: |---->| (Full Schema) |
| | | SELECT only | | |
+----------------+ | on specific cols| +----------------+| & rows) |+----------------+
- 用户/应用只有对视图的 SELECT 权限。
- 视图定义了哪些列(如隐藏了 Salary 列)和哪些行(如只显示 Active=1)对用户可见。
- 用户通过视图间接访问基础表,但只能看到视图定义允许的部分。
六、SQL 访问控制
SQL 访问控制
SQL 访问控制是数据库安全的重要组成部分,主要用于管理用户对数据库对象的访问权限。
一、用户管理
作用:创建、修改和删除数据库用户
关键字:CREATE USER、ALTER USER、DROP USER、SHOW CREATE USER
1. 用户管理语法
sql
-- 创建用户
CREATE USER 'username'@'host' IDENTIFIED BY 'password';-- 修改用户密码
ALTER USER 'username'@'host' IDENTIFIED BY 'new_password';-- 重命名用户
RENAME USER 'old_username'@'host' TO 'new_username'@'host';-- 删除用户
DROP USER 'username'@'host';-- 查看用户创建语句
SHOW CREATE USER 'username'@'host';
2. 示例
sql
-- 创建用户testuser,只能从本地访问
CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'test123';-- 修改用户密码
ALTER USER 'testuser'@'localhost' IDENTIFIED BY 'newtest123';-- 重命名用户
RENAME USER 'testuser'@'localhost' TO 'newuser'@'localhost';-- 删除用户
DROP USER 'newuser'@'localhost';
二、权限管理
作用:授予、撤销用户对数据库对象的访问权限
关键字:GRANT、REVOKE、SHOW GRANTS
1. 常用权限类型
权限类型 | 作用范围 |
---|---|
SELECT | 查询数据 |
INSERT | 插入数据 |
UPDATE | 修改数据 |
DELETE | 删除数据 |
CREATE | 创建表、视图等对象 |
DROP | 删除表、视图等对象 |
ALTER | 修改表结构 |
INDEX | 创建和删除索引 |
ALL PRIVILEGES | 所有权限 |
GRANT OPTION | 授予他人权限的权限 |
2. 权限管理语法
sql
-- 授予权限
GRANT privilege_type ON database.table TO 'username'@'host';-- 授予权限并允许传播
GRANT privilege_type ON database.table TO 'username'@'host' WITH GRANT OPTION;-- 撤销权限
REVOKE privilege_type ON database.table FROM 'username'@'host';-- 撤销所有权限
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'username'@'host';-- 查看用户权限
SHOW GRANTS FOR 'username'@'host';-- 查看当前用户权限
SHOW GRANTS;
3. 示例
-- 授予用户对testdb数据库所有表的SELECT权限
GRANT SELECT ON testdb.* TO 'testuser'@'localhost';-- 授予用户对testdb数据库中user表的INSERT和UPDATE权限
GRANT INSERT, UPDATE ON testdb.user TO 'testuser'@'localhost';-- 授予用户所有权限并允许传播
GRANT ALL PRIVILEGES ON testdb.* TO 'testuser'@'localhost' WITH GRANT OPTION;-- 撤销用户的DELETE权限
REVOKE DELETE ON testdb.* FROM 'testuser'@'localhost';-- 撤销用户的GRANT OPTION权限
REVOKE GRANT OPTION ON testdb.* FROM 'testuser'@'localhost';-- 撤销用户所有权限
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser'@'localhost';-- 查看用户权限
SHOW GRANTS FOR 'testuser'@'localhost';
三、角色管理
作用:简化权限管理,将权限分配给角色,再将角色分配给用户
关键字:CREATE ROLE、DROP ROLE、GRANT ROLE、SET DEFAULT ROLE、REVOKE ROLE
1. 角色管理语法
sql
-- 创建角色
CREATE ROLE 'role_name';-- 授予角色权限
GRANT privilege_type ON database.table TO 'role_name';-- 将角色授予用户
GRANT 'role_name' TO 'username'@'host';-- 设置用户默认角色
SET DEFAULT ROLE 'role_name' TO 'username'@'host';-- 设置用户激活角色
SET ROLE 'role_name';
SET ROLE ALL; -- 激活所有角色-- 撤销角色权限
REVOKE privilege_type ON database.table FROM 'role_name';-- 撤销用户角色
REVOKE 'role_name' FROM 'username'@'host';-- 删除角色
DROP ROLE 'role_name';
2. 示例
sql
-- 创建只读角色
CREATE ROLE 'read_only';-- 授予只读权限
GRANT SELECT ON testdb.* TO 'read_only';-- 创建读写角色
CREATE ROLE 'read_write';-- 授予读写权限
GRANT SELECT, INSERT, UPDATE, DELETE ON testdb.* TO 'read_write';-- 将角色授予用户
GRANT 'read_only' TO 'user1'@'localhost';
GRANT 'read_write' TO 'user2'@'localhost';-- 设置默认角色
SET DEFAULT ROLE 'read_write' TO 'user2'@'localhost';-- 撤销角色权限
REVOKE INSERT, UPDATE, DELETE ON testdb.* FROM 'read_write';-- 撤销用户角色
REVOKE 'read_only' FROM 'user1'@'localhost';-- 删除角色
DROP ROLE 'read_only';
DROP ROLE 'read_write';
四、SQL 访问控制流程图
下面是 SQL 访问控制的流程示意图:
使用注意事项
- 最小权限原则:为用户分配完成工作所需的最小权限集
- 定期审计:定期检查用户权限,撤销不再需要的权限
- 密码安全:使用强密码策略,定期更换密码
- 权限继承:角色权限变更会影响所有关联用户,需谨慎操作
- 权限传播:慎用 WITH GRANT OPTION,避免权限失控
- DROP USER 注意:删除用户前应确认其权限已清理,数据已备份
七、嵌入式SQL
嵌入式 SQL 是将 SQL 语句直接嵌入到高级程序设计语言(如 C、Java、Python 等)中,实现数据库操作的技术。它允许程序员在高级语言环境中直接使用 SQL 访问数据库,结合了高级语言的流程控制能力和 SQL 的数据处理能力。
一、嵌入式 SQL 基本概念
- 宿主语言:嵌入 SQL 的高级程序设计语言(如 C、Java)
- SQL 通信区:用于在宿主语言和 SQL 之间传递状态信息
- 主变量:宿主语言中定义的变量,用于 SQL 语句中传递数据
- 游标:用于处理 SQL 查询返回的多行结果
二、嵌入式 SQL 语法特点
-
语句标识:使用特殊标记区分 SQL 语句和宿主语言代码
- C 语言中通常使用
EXEC SQL
开头 - Java 中使用 JDBC API
- Python 中使用 DB API
- C 语言中通常使用
-
主变量使用:在 SQL 语句中使用宿主语言变量
运行
EXEC SQL SELECT name, age INTO :name, :age FROM users WHERE id = :user_id;
-
游标操作:处理多行查询结果
运行
EXEC SQL DECLARE user_cursor CURSOR FOR SELECT id, name FROM users;EXEC SQL OPEN user_cursor;while (1) {EXEC SQL FETCH user_cursor INTO :id, :name;if (sqlca.sqlcode != 0) break;// 处理数据 }EXEC SQL CLOSE user_cursor;
三、嵌入式 SQL 工作流程
四、不同语言中的嵌入式 SQL 实现
-
C 语言中的嵌入式 SQL
#include <stdio.h> EXEC SQL INCLUDE SQLCA; // SQL通信区int main() {char username[20] = "test";int age;EXEC SQL BEGIN DECLARE SECTION;char user[20];int user_age;EXEC SQL END DECLARE SECTION;EXEC SQL CONNECT TO database@server USER username;EXEC SQL SELECT age INTO :user_age FROM users WHERE name = :username;if (sqlca.sqlcode == 0) {printf("用户年龄: %d\n", user_age);}EXEC SQL COMMIT WORK;EXEC SQL DISCONNECT;return 0; }
-
Java 中的嵌入式 SQL(JDBC)
java
import java.sql.*;public class EmbeddedSQLExample {public static void main(String[] args) {String url = "jdbc:mysql://localhost:3306/mydb";String username = "root";String password = "password";try (Connection conn = DriverManager.getConnection(url, username, password)) {String sql = "SELECT name, age FROM users WHERE id = ?";try (PreparedStatement pstmt = conn.prepareStatement(sql)) {pstmt.setInt(1, 1); // 设置参数try (ResultSet rs = pstmt.executeQuery()) {if (rs.next()) {String name = rs.getString("name");int age = rs.getInt("age");System.out.printf("用户: %s, 年龄: %d%n", name, age);}}}} catch (SQLException e) {e.printStackTrace();}} }
-
Python 中的嵌入式 SQL(SQLite 示例)
import sqlite3conn = sqlite3.connect('example.db') cursor = conn.cursor()# 执行SQL查询 user_id = 1 cursor.execute("SELECT name, age FROM users WHERE id = ?", (user_id,))# 获取结果 row = cursor.fetchone() if row:name, age = rowprint(f"用户: {name}, 年龄: {age}")conn.close()
五、嵌入式 SQL 的优缺点
优点:
- 无缝集成高级语言和数据库功能
- 利用宿主语言的强大编程能力
- 执行效率高(预处理优化)
- 便于实现复杂业务逻辑
缺点:
- 代码可读性较差
- 数据库移植性受限
- 调试和维护难度较大
- 事务管理复杂
六、适用场景
- 需要高性能数据处理的应用
- 对事务处理要求严格的系统
- 与现有数据库架构深度集成的应用
- 资源受限环境下的数据库操作
嵌入式 SQL 是数据库编程的重要技术,现代开发中更常见的是通过 ORM(对象关系映射)工具实现类似功能,但在性能敏感场景和遗留系统中仍广泛使用。