庆阳市网站建设_网站建设公司_悬停效果_seo优化
2026/1/19 14:28:36 网站建设 项目流程

# MySQL 语句优化方式详解

## 📊 **一、SQL 编写优化**

### **1. 避免 SELECT ***
```sql
-- ❌ 不推荐
SELECT * FROM users WHERE status = 1;

-- ✅ 推荐:只取需要的列
SELECT id, name, email FROM users WHERE status = 1;
```

### **2. 使用 EXISTS 代替 IN**
```sql
-- ❌ 子查询数据量大时性能差
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);

-- ✅ 推荐:EXISTS 通常更高效
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id AND o.amount > 1000
);

-- ✅ 更优:使用 JOIN
SELECT DISTINCT u.* FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.amount > 1000;
```

### **3. 避免在 WHERE 中使用函数**
```sql
-- ❌ 函数导致索引失效
SELECT * FROM orders WHERE DATE(create_time) = '2024-01-19';
SELECT * FROM users WHERE LOWER(name) = 'john';

-- ✅ 推荐:将函数移至右侧
SELECT * FROM orders
WHERE create_time >= '2024-01-19'
AND create_time < '2024-01-20';

SELECT * FROM users
WHERE name = 'JOHN' OR name = 'john'; -- 或存储时统一大小写
```

### **4. 使用 LIMIT 分页优化**
```sql
-- ❌ 传统分页(大数据量偏移时慢)
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;

-- ✅ 推荐:使用游标分页
SELECT * FROM orders
WHERE id > 1000000 -- 上次查询的最后一个id
ORDER BY id LIMIT 20;

-- ✅ 或使用覆盖索引
SELECT t.* FROM orders t
JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) tmp
ON t.id = tmp.id;
```

## 🔍 **二、索引优化**

### **1. 创建合适索引**
```sql
-- 多列查询时创建复合索引
-- ❌ 单列索引效率低
CREATE INDEX idx_user ON orders(user_id);
CREATE INDEX idx_status ON orders(status);

-- ✅ 推荐:复合索引(注意顺序)
CREATE INDEX idx_user_status ON orders(user_id, status);

-- 查询示例:可高效使用索引
EXPLAIN SELECT * FROM orders
WHERE user_id = 100 AND status = 1; -- 使用索引
EXPLAIN SELECT * FROM orders
WHERE status = 1; -- 无法使用索引(不满足最左前缀)
```

### **2. 覆盖索引优化**
```sql
-- ❌ 需要回表
SELECT * FROM users WHERE age > 20;

-- ✅ 推荐:使用覆盖索引
CREATE INDEX idx_age_name ON users(age, name);

-- 查询只需索引列,无需回表
SELECT age, name FROM users WHERE age > 20;
EXPLAIN SELECT age, name FROM users WHERE age > 20;
-- Extra: Using index
```

### **3. 索引选择性优化**
```sql
-- 低选择性列不适合单独建索引
SELECT COUNT(DISTINCT gender) / COUNT(*) FROM users; -- 选择性低
SELECT COUNT(DISTINCT email) / COUNT(*) FROM users; -- 选择性高

-- 创建前缀索引(长文本字段)
CREATE INDEX idx_email_prefix ON users(email(20));
```

## 🎯 **三、JOIN 优化**

### **1. JOIN 顺序优化**
```sql
-- ❌ 大表在前
SELECT * FROM large_table l
JOIN small_table s ON l.id = s.large_id;

-- ✅ 推荐:小表驱动大表
SELECT * FROM small_table s
JOIN large_table l ON s.large_id = l.id;
```

### **2. 避免多重子查询**
```sql
-- ❌ 多重子查询
SELECT u.name,
(SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) as order_count,
(SELECT SUM(amount) FROM orders o WHERE o.user_id = u.id) as total_amount
FROM users u;

-- ✅ 推荐:使用 JOIN + GROUP BY
SELECT u.name,
COUNT(o.id) as order_count,
SUM(o.amount) as total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;
```

## 📈 **四、表结构优化**

### **1. 适当使用冗余字段**
```sql
-- 订单表增加用户名字段,避免频繁 JOIN
ALTER TABLE orders ADD COLUMN user_name VARCHAR(50);

-- 更新逻辑
UPDATE orders o
JOIN users u ON o.user_id = u.id
SET o.user_name = u.name
WHERE o.id = ?;
```

### **2. 垂直分表**
```sql
-- 原始表(包含不常用字段)
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100),
profile_text TEXT, -- 不常查询的大字段
settings JSON
);

-- 优化:拆分为两个表
CREATE TABLE user_basic (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100)
);

CREATE TABLE user_detail (
user_id INT PRIMARY KEY,
profile_text TEXT,
settings JSON,
FOREIGN KEY (user_id) REFERENCES user_basic(id)
);
```

## 🔧 **五、查询执行优化**

### **1. 使用批量操作**
```sql
-- ❌ 多次单条插入
INSERT INTO logs (message) VALUES ('log1');
INSERT INTO logs (message) VALUES ('log2');

-- ✅ 推荐:批量插入
INSERT INTO logs (message) VALUES
('log1'), ('log2'), ('log3'), ('log4');

-- ❌ 循环更新
UPDATE products SET stock = stock - 1 WHERE id = 1;
UPDATE products SET stock = stock - 1 WHERE id = 2;

-- ✅ 推荐:批量更新
UPDATE products
SET stock = stock - 1
WHERE id IN (1, 2, 3);
```

### **2. 合理使用 UNION/UNION ALL**
```sql
-- ❌ UNION 会去重排序(性能开销)
SELECT id FROM active_users
UNION
SELECT id FROM inactive_users;

-- ✅ 推荐:UNION ALL 更快(如果不需要去重)
SELECT id FROM active_users
UNION ALL
SELECT id FROM inactive_users;
```

## 📋 **六、EXPLAIN 分析实战**

```sql
-- 1. 查看执行计划
EXPLAIN FORMAT=JSON
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.create_time > '2024-01-01'
GROUP BY u.id
HAVING COUNT(o.id) > 5
ORDER BY u.name;

-- 2. 分析关键指标
-- type: system > const > eq_ref > ref > range > index > ALL
-- Extra: Using filesort, Using temporary 需要关注
```

## 🎯 **七、优化 checklist**

1. **索引使用**
- WHERE/JOIN 条件是否有合适索引
- 是否使用覆盖索引
- 索引选择性是否足够高

2. **查询逻辑**
- 是否避免了 SELECT *
- 是否优化了子查询
- 分页查询是否优化

3. **表结构**
- 字段类型是否合适(避免过大)
- 是否需要垂直/水平分表
- 是否有合适的冗余字段

4. **执行计划**
- 是否出现全表扫描(type=ALL)
- 是否出现文件排序(Using filesort)
- 是否使用临时表(Using temporary)

## 💡 **优化建议流程**
```sql
-- 1. 找出慢查询
SHOW PROCESSLIST;
-- 或开启慢查询日志

-- 2. 使用 EXPLAIN 分析
EXPLAIN SELECT ...;

-- 3. 针对性优化
-- 添加索引、重写SQL、调整表结构

-- 4. 验证优化效果
-- 再次执行 EXPLAIN,对比优化前后
```

通过以上优化方式,通常可以显著提升查询性能。关键是:**分析执行计划,理解数据特点,针对性优化**。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询