Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL8-SOP

  • MySQL实战45讲学习笔记

    • MySQL45讲学习笔记
    • MySQL基础架构
    • MySQL日志系统 (Redo Log 与 Binlog)
    • MySQL字符串字段索引优化
    • MySQL索引原理与优化
    • MySQL锁机制详解
    • MySQL事务与MVCC机制
    • MySQL普通索引与唯一索引的选择
    • MySQL优化器如何选择索引
    • MySQL抖动刷脏页
    • 表空间管理与回收
    • count函数详解
    • 日志索引
    • orderby工作原理
    • 随机排序实现
    • SQL性能差异函数与转换
      • 1. 函数对SQL性能的影响
        • 1.1 函数导致的索引失效
        • 1.2 函数计算开销
        • 1.3 复杂函数的性能问题
      • 2. 数据类型转换对性能的影响
        • 2.1 字符串与数字转换
        • 2.2 字符集转换
        • 2.3 时间类型转换
      • 3. 性能差异分析工具
        • 3.1 EXPLAIN分析
        • 3.2 查询执行时间分析
        • 3.3 性能监控
      • 4. 函数优化策略
        • 4.1 避免在WHERE子句中使用函数
        • 4.2 使用索引优化函数表达式
        • 4.3 预计算和缓存
      • 5. 数据类型转换优化
        • 5.1 字段类型匹配
        • 5.2 转换函数的使用
        • 5.3 字符集优化
      • 6. 复杂场景优化
        • 6.1 多字段组合查询
        • 6.2 子查询中的函数
        • 6.3 CASE语句优化
      • 7. 性能测试与验证
        • 7.1 基准测试
        • 7.2 执行计划对比
      • 8. 最佳实践建议
        • 8.1 查询设计原则
        • 8.2 索引策略
        • 8.3 监控和优化
        • 8.4 应用层优化
      • 9. 常见陷阱和避免方法
        • 9.1 隐式类型转换
        • 9.2 字符串比较优化
        • 9.3 空值处理
      • 10. 总结
    • 慢查询分析锁与版本
    • 幻读与间隙锁
    • 加锁规则分析
    • 应急性能优化方法
    • 数据持久化保证
    • 主备一致性原理
    • 高可用架构与切换
    • 备库延迟分析与优化
    • 主备切换GTID
    • 读写分离实践与问题
    • 数据库健康检查
    • 锁与死锁
    • 数据误删恢复
    • Kill命令详解
    • 查询与内存使用分析
    • Join原理与选择
    • 临时表原理与应用
    • 内部临时表详解
    • Memory引擎详解
    • 自增ID详解
    • Insert加锁分析
    • 表复制方法比较
    • Grant与权限管理
    • 分区表详解
    • SQL语句中的Join问题
    • 自增ID用尽问题
  • 专题系列
  • MySQL实战45讲学习笔记
Carry の Blog
2024-07-27
目录

SQL性能差异函数与转换

# SQL性能差异函数与转换

在MySQL数据库中,SQL语句的性能差异往往源于函数使用、数据类型转换等因素。本文将深入分析这些因素对查询性能的影响,并提供相应的优化策略。

# 1. 函数对SQL性能的影响

# 1.1 函数导致的索引失效

-- 问题示例:函数导致索引失效
SELECT * FROM users WHERE YEAR(created_date) = 2024;
-- 等价于
SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';

-- 优化方案:避免在索引字段上使用函数
SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';
1
2
3
4
5
6
7

# 1.2 函数计算开销

-- 函数计算开销示例
-- 不好的做法
SELECT UPPER(name), LOWER(email) FROM users WHERE age > 25;

-- 更好的做法(如果需要)
SELECT name, email FROM users WHERE age > 25;
-- 在应用层进行大小写转换

-- 或者使用索引优化
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
-- 优化为
SELECT * FROM users WHERE name = 'John';
1
2
3
4
5
6
7
8
9
10
11
12

# 1.3 复杂函数的性能问题

-- 复杂函数示例
-- 性能较差
SELECT *, 
       CONCAT(first_name, ' ', last_name) as full_name,
       DATE_FORMAT(birth_date, '%Y-%m-%d') as formatted_birth_date
FROM users 
WHERE YEAR(birth_date) = 2000;

-- 优化后
SELECT *,
       CONCAT(first_name, ' ', last_name) as full_name,
       birth_date as formatted_birth_date
FROM users 
WHERE birth_date >= '2000-01-01' AND birth_date < '2001-01-01';
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2. 数据类型转换对性能的影响

# 2.1 字符串与数字转换

-- 问题示例:数据类型转换
SELECT * FROM orders WHERE order_id = '12345';
-- 如果order_id是INT类型,MySQL需要进行类型转换

-- 优化方案:保持数据类型一致
SELECT * FROM orders WHERE order_id = 12345;
1
2
3
4
5
6

# 2.2 字符集转换

-- 字符集转换问题
SELECT * FROM products WHERE product_name = 'iPhone 12';

-- 如果product_name是latin1字符集,而查询是utf8字符集
-- 会产生字符集转换开销

-- 优化方案:保持字符集一致
-- 在查询中明确指定字符集
SELECT * FROM products WHERE CONVERT(product_name USING utf8) = 'iPhone 12';
1
2
3
4
5
6
7
8
9

# 2.3 时间类型转换

-- 时间类型转换示例
-- 不好的做法
SELECT * FROM logs WHERE DATE(log_time) = '2024-01-01';

-- 优化方案:避免函数使用
SELECT * FROM logs WHERE log_time >= '2024-01-01' AND log_time < '2024-01-02';

-- 更好的做法:使用范围查询
SELECT * FROM logs WHERE log_time BETWEEN '2024-01-01' AND '2024-01-01 23:59:59';
1
2
3
4
5
6
7
8
9

# 3. 性能差异分析工具

# 3.1 EXPLAIN分析

-- 使用EXPLAIN分析函数对性能的影响
EXPLAIN SELECT * FROM users WHERE YEAR(created_date) = 2024;
-- 查看type字段是否为ALL(全表扫描)

EXPLAIN SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';
-- 查看type字段是否为range(范围扫描)
1
2
3
4
5
6

# 3.2 查询执行时间分析

-- 分析查询执行时间
SET profiling = 1;

SELECT * FROM users WHERE YEAR(created_date) = 2024;
SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';

SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;  -- 分析第一个查询
SHOW PROFILE FOR QUERY 2;  -- 分析第二个查询
1
2
3
4
5
6
7
8
9

# 3.3 性能监控

-- 监控慢查询
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log_file';
1
2
3
4
5
6

# 4. 函数优化策略

# 4.1 避免在WHERE子句中使用函数

-- 问题示例
SELECT * FROM orders WHERE DAY(order_date) = 15;

-- 优化方案
SELECT * FROM orders WHERE order_date >= '2024-01-15' AND order_date < '2024-01-16';

-- 更好的范围查询
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-15' AND '2024-01-15 23:59:59';
1
2
3
4
5
6
7
8

# 4.2 使用索引优化函数表达式

-- 创建函数索引(MySQL 8.0+)
CREATE INDEX idx_year_created ON users (YEAR(created_date));

-- 或者使用虚拟列
ALTER TABLE users ADD COLUMN year_created INT AS (YEAR(created_date)) STORED;
CREATE INDEX idx_year_created ON users (year_created);
1
2
3
4
5
6

# 4.3 预计算和缓存

-- 预计算优化
-- 不好的做法:每次查询都计算
SELECT *, 
       (price * quantity) as total,
       (price * quantity * tax_rate) as total_with_tax
FROM order_items;

-- 好的做法:预先计算并存储
ALTER TABLE order_items ADD COLUMN total DECIMAL(10,2);
ALTER TABLE order_items ADD COLUMN total_with_tax DECIMAL(10,2);

UPDATE order_items 
SET total = price * quantity,
    total_with_tax = price * quantity * tax_rate;

-- 查询时直接使用
SELECT total, total_with_tax FROM order_items;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5. 数据类型转换优化

# 5.1 字段类型匹配

-- 检查字段类型
DESCRIBE users;
-- 确保查询时使用正确的数据类型

-- 不好的做法
SELECT * FROM users WHERE user_id = '12345';

-- 好的做法
SELECT * FROM users WHERE user_id = 12345;

-- 检查字段类型
SHOW CREATE TABLE users;
1
2
3
4
5
6
7
8
9
10
11
12

# 5.2 转换函数的使用

-- 合理使用转换函数
-- 日期转换
SELECT * FROM orders WHERE order_date = STR_TO_DATE('2024-01-01', '%Y-%m-%d');

-- 数值转换
SELECT * FROM products WHERE price = CAST('99.99' AS DECIMAL(10,2));

-- 字符串转换
SELECT * FROM users WHERE user_name = CONVERT('john', CHAR CHARACTER SET utf8mb4);
1
2
3
4
5
6
7
8
9

# 5.3 字符集优化

-- 检查字符集设置
SHOW CREATE TABLE products;
SHOW VARIABLES LIKE 'character_set%';

-- 优化字符集使用
-- 在连接时指定字符集
SET NAMES utf8mb4;

-- 或者在连接字符串中指定
-- mysql://user:password@host:port/db?charset=utf8mb4
1
2
3
4
5
6
7
8
9
10

# 6. 复杂场景优化

# 6.1 多字段组合查询

-- 问题示例:多字段函数操作
SELECT * FROM users 
WHERE CONCAT(first_name, ' ', last_name) = 'John Smith';

-- 优化方案:使用LIKE或范围查询
SELECT * FROM users 
WHERE first_name = 'John' AND last_name = 'Smith';

-- 或者使用全文索引
ALTER TABLE users ADD FULLTEXT(first_name, last_name);
SELECT * FROM users 
WHERE MATCH(first_name, last_name) AGAINST('John Smith');
1
2
3
4
5
6
7
8
9
10
11
12

# 6.2 子查询中的函数

-- 问题示例:子查询中的函数
SELECT * FROM orders 
WHERE customer_id IN (
    SELECT customer_id FROM customers 
    WHERE YEAR(registration_date) = 2024
);

-- 优化方案:避免函数使用
SELECT o.* FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE c.registration_date >= '2024-01-01' AND c.registration_date < '2025-01-01';
1
2
3
4
5
6
7
8
9
10
11

# 6.3 CASE语句优化

-- 问题示例:CASE语句中的复杂计算
SELECT 
    CASE 
        WHEN age < 18 THEN 'Minor'
        WHEN age BETWEEN 18 AND 65 THEN 'Adult'
        ELSE 'Senior'
    END as age_group,
    name
FROM users;

-- 优化方案:预计算
ALTER TABLE users ADD COLUMN age_group VARCHAR(20);
UPDATE users SET age_group = 
    CASE 
        WHEN age < 18 THEN 'Minor'
        WHEN age BETWEEN 18 AND 65 THEN 'Adult'
        ELSE 'Senior'
    END;

-- 查询时直接使用
SELECT age_group, name FROM users;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 7. 性能测试与验证

# 7.1 基准测试

-- 创建测试数据
CREATE TABLE test_performance (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_date DATE,
    status VARCHAR(20)
);

-- 插入测试数据
INSERT INTO test_performance (name, created_date, status) 
VALUES 
('User1', '2024-01-01', 'active'),
('User2', '2024-01-02', 'inactive'),
-- ... 更多数据

-- 性能测试
SET @start_time = NOW();
SELECT * FROM test_performance WHERE YEAR(created_date) = 2024;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as function_time;

SET @start_time = NOW();
SELECT * FROM test_performance WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as direct_time;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 7.2 执行计划对比

-- 对比执行计划
EXPLAIN SELECT * FROM users WHERE YEAR(created_date) = 2024;
EXPLAIN SELECT * FROM users WHERE created_date >= '2024-01-01' AND created_date < '2025-01-01';

-- 查看详细信息
EXPLAIN FORMAT=JSON 
SELECT * FROM users WHERE YEAR(created_date) = 2024;
1
2
3
4
5
6
7

# 8. 最佳实践建议

# 8.1 查询设计原则

-- 1. 避免在WHERE子句中使用函数
-- 2. 保持数据类型一致
-- 3. 合理使用索引
-- 4. 预计算复杂表达式

-- 示例:优化的查询设计
-- 原始查询
SELECT * FROM orders 
WHERE DATE_FORMAT(order_date, '%Y-%m') = '2024-01';

-- 优化后
SELECT * FROM orders 
WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01';
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.2 索引策略

-- 为避免函数索引失效,创建合适的索引
-- 日期范围查询
CREATE INDEX idx_orders_date ON orders (order_date);

-- 多字段查询
CREATE INDEX idx_orders_customer_date ON orders (customer_id, order_date);

-- 复合索引优化
CREATE INDEX idx_users_status_age ON users (status, age);
1
2
3
4
5
6
7
8
9

# 8.3 监控和优化

-- 创建性能监控脚本
DELIMITER //
CREATE PROCEDURE monitor_function_performance()
BEGIN
    DECLARE slow_query_count INT DEFAULT 0;
    
    -- 检查使用函数的慢查询
    SELECT COUNT(*) INTO slow_query_count
    FROM performance_schema.events_statements_history_long 
    WHERE DIGEST_TEXT LIKE '%YEAR(%' 
    OR DIGEST_TEXT LIKE '%MONTH(%' 
    OR DIGEST_TEXT LIKE '%DAY(%';
    
    IF slow_query_count > 0 THEN
        INSERT INTO performance_alert (message, timestamp) 
        VALUES (CONCAT('Found ', slow_query_count, ' queries with date functions'), NOW());
    END IF;
END //
DELIMITER ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 8.4 应用层优化

# Python示例:应用层优化
import datetime

# 不好的做法:在数据库中计算
# query = "SELECT * FROM users WHERE YEAR(created_date) = 2024"

# 好的做法:在应用层计算
current_year = datetime.datetime.now().year
query = "SELECT * FROM users WHERE created_date >= %s AND created_date < %s"
params = (f"{current_year}-01-01", f"{current_year+1}-01-01")
1
2
3
4
5
6
7
8
9
10

# 9. 常见陷阱和避免方法

# 9.1 隐式类型转换

-- 隐式类型转换问题
SELECT * FROM orders WHERE order_id = '12345';
-- 如果order_id是INT类型,会产生隐式转换

-- 明确类型转换
SELECT * FROM orders WHERE order_id = 12345;
1
2
3
4
5
6

# 9.2 字符串比较优化

-- 问题示例
SELECT * FROM users WHERE UPPER(name) = 'JOHN';

-- 优化方案
SELECT * FROM users WHERE name = 'John';
-- 或者创建索引
CREATE INDEX idx_users_name_upper ON users (UPPER(name));
1
2
3
4
5
6
7

# 9.3 空值处理

-- 空值处理的性能考虑
-- 不好的做法
SELECT * FROM users WHERE UPPER(name) IS NOT NULL;

-- 好的做法
SELECT * FROM users WHERE name IS NOT NULL;
1
2
3
4
5
6

# 10. 总结

SQL性能差异主要来源于函数使用和数据类型转换。通过理解这些机制,可以有效优化查询性能。

关键优化策略包括:

  1. 避免在WHERE子句中使用函数
  2. 保持数据类型一致性
  3. 合理使用索引
  4. 预计算复杂表达式
  5. 使用EXPLAIN分析执行计划
  6. 监控慢查询并及时优化

通过遵循这些最佳实践,可以显著提升MySQL查询性能,减少系统资源消耗,提高用户体验。

#MySQL#SQL性能#函数优化#查询优化#学习笔记
上次更新: 3/4/2026

← 随机排序实现 慢查询分析锁与版本→

最近更新
01
表空间管理与回收
03-04
02
MySQL抖动刷脏页
03-04
03
count函数详解
03-04
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式