1. 为什么需要整理MySQL常用SQL
在日常数据库操作中,我们经常会遇到一些重复性的SQL编写需求。作为从业十年的数据库工程师,我见过太多团队因为缺乏规范的SQL参考模板而导致的效率低下问题。开发人员每次都要重新编写基础查询,不仅浪费时间,还容易因为写法不一致引发潜在的性能问题。
这个SQL汇总清单是我在金融、电商等多个行业项目中积累的实战结晶,涵盖了90%的日常数据库操作场景。特别适合初中级开发人员作为案头参考,也能帮助DBA统一团队SQL编写规范。下面这些语句都经过生产环境验证,在百万级数据量的表上依然保持良好性能。
2. 基础查询语句精要
2.1 数据检索的黄金组合
最基础的SELECT语句其实藏着不少优化点。这是经过优化的查询模板:
SELECT column1, column2 /* 只选择必要字段 */ FROM table_name WHERE status = 1 /* 优先使用等值查询 */ AND create_time > '2023-01-01' /* 范围查询放后面 */ AND name LIKE '张%' /* 左匹配索引有效 */ ORDER BY id DESC /* 排序字段要有索引 */ LIMIT 20 OFFSET 0; /* 分页必备 */关键技巧:
- 字段选择遵循"最小化原则",避免SELECT *
- WHERE条件顺序影响性能:等值查询→范围查询→LIKE
- 分页查询必带LIMIT,大数据量时改用WHERE id > last_id模式
2.2 多表关联查询实战
JOIN操作是SQL复杂度的分水岭。推荐使用显式JOIN语法:
SELECT o.order_no, u.user_name, p.product_name FROM orders o INNER JOIN users u ON o.user_id = u.id /* 内连接首选 */ LEFT JOIN products p ON o.product_id = p.id /* 需保留左表数据时 */ WHERE o.status = 2 AND u.vip_level > 3;注意事项:
- 关联字段必须有索引,且类型必须一致
- 小表驱动大表原则:数据量小的表放在JOIN前面
- 避免3张表以上关联,复杂查询建议拆分为多次查询
3. 数据操作语句最佳实践
3.1 安全的批量更新方案
直接UPDATE全表是危险的,这是我惯用的安全模式:
UPDATE products SET price = price * 0.9, update_time = NOW() WHERE category_id = 5 AND stock > 100 LIMIT 1000; /* 重要!限制影响行数 */执行建议:
- 先SELECT确认影响范围再UPDATE
- 大批量更新分批次执行,添加LIMIT限制
- 必须包含更新人、更新时间等审计字段
3.2 高性能插入方案对比
不同场景下的插入方案选择:
/* 单条插入 */ INSERT INTO users (name, age) VALUES ('张三', 25); /* 批量插入(推荐) */ INSERT INTO users (name, age) VALUES ('李四', 30), ('王五', 28), ('赵六', 35); /* 替代重复数据 */ INSERT IGNORE INTO users (id, name) VALUES (1, '张三'); /* 主键冲突时跳过 */ /* 更新重复数据 */ INSERT INTO users (id, name) VALUES (1, '张三新名字') ON DUPLICATE KEY UPDATE name = VALUES(name);性能实测对比(10万条数据):
| 方式 | 耗时 | 特点 |
|---|---|---|
| 单条插入 | 85s | 事务开销大 |
| 批量插入(1000) | 1.2s | 推荐值100-1000条/批 |
| LOAD DATA | 0.8s | 需要文件权限 |
4. 数据定义语句规范
4.1 建表语句的工业级模板
这是我使用的标准建表模板(含注释版):
CREATE TABLE `order_detail` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键', `order_no` varchar(32) NOT NULL COMMENT '订单编号', `user_id` bigint(20) NOT NULL COMMENT '用户ID', `amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '金额', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-待支付', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_user_id` (`user_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';设计要点:
- 必须包含主键和注释
- 时间字段自动更新
- 字符集统一用utf8mb4
- 索引按实际查询需求设计
4.2 在线DDL操作指南
生产环境修改表结构的安全做法:
/* 添加字段 */ ALTER TABLE users ADD COLUMN mobile varchar(20) COMMENT '手机号' AFTER email, ALGORITHM=INPLACE, LOCK=NONE; /* 在线DDL关键参数 */ /* 修改字段 */ ALTER TABLE products CHANGE COLUMN price original_price decimal(10,2), ALGORITHM=INPLACE; /* 创建索引 */ CREATE INDEX idx_product_name ON products(name) ALGORITHM=INPLACE;注意事项:
- 必须指定ALGORITHM和LOCK参数
- 避免同时修改多个字段
- 大表操作选择业务低峰期
- 先备份再执行
5. 高级查询技巧
5.1 窗口函数实战应用
MySQL 8.0+的窗口函数能简化复杂查询:
/* 计算各部门薪资排名 */ SELECT name, department, salary, RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank FROM employees; /* 计算移动平均值 */ SELECT date, sales, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM daily_sales;典型应用场景:
- 分组排名(替代子查询)
- 累计计算(替代自连接)
- 差值分析(同期对比)
5.2 递归查询处理树形数据
处理组织架构等层级数据:
WITH RECURSIVE org_tree AS ( /* 基础查询:查找根节点 */ SELECT id, name, parent_id, 1 AS level FROM organization WHERE parent_id = 0 UNION ALL /* 递归查询:关联子节点 */ SELECT o.id, o.name, o.parent_id, ot.level + 1 FROM organization o JOIN org_tree ot ON o.parent_id = ot.id ) SELECT * FROM org_tree ORDER BY level, id;优化建议:
- 层级字段添加索引
- 设置递归深度限制:SET @@cte_max_recursion_depth = 100
- 大数据量考虑改用预计算路径枚举法
6. 事务与锁机制实战
6.1 事务控制的正确姿势
银行转账场景的标准写法:
START TRANSACTION; /* 查询账户余额(加锁) */ SELECT balance FROM accounts WHERE user_id = 1001 FOR UPDATE; /* 执行转账操作 */ UPDATE accounts SET balance = balance - 500 WHERE user_id = 1001; UPDATE accounts SET balance = balance + 500 WHERE user_id = 1002; /* 记录交易流水 */ INSERT INTO transactions VALUES(...); COMMIT;关键原则:
- 事务要尽可能短
- 先SELECT FOR UPDATE锁定资源
- 避免在事务中包含外部调用
- 设置合适的隔离级别
6.2 死锁分析与预防
典型死锁场景重现:
-- 会话1 START TRANSACTION; UPDATE products SET stock = stock - 1 WHERE id = 1; -- 持有id=1的锁 UPDATE products SET stock = stock - 1 WHERE id = 2; -- 等待id=2的锁 -- 会话2 START TRANSACTION; UPDATE products SET stock = stock - 1 WHERE id = 2; -- 持有id=2的锁 UPDATE products SET stock = stock - 1 WHERE id = 1; -- 等待id=1的锁预防措施:
- 按固定顺序访问资源(如总是先操作id小的记录)
- 降低事务隔离级别
- 添加合理的索引减少锁定范围
- 设置锁等待超时:innodb_lock_wait_timeout = 5
7. 性能优化专项
7.1 EXPLAIN执行计划解读
分析查询性能的标准流程:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE user_id = 100 AND status = 2; /* 输出关键字段说明: - type: 访问类型(const > ref > range > index > ALL) - key: 实际使用的索引 - rows: 预估扫描行数 - Extra: 额外信息(Using filesort需警惕) */优化案例:
-- 优化前:全表扫描 EXPLAIN SELECT * FROM users WHERE DATE(create_time) = '2023-01-01'; -- 优化后:索引扫描 EXPLAIN SELECT * FROM users WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-01 23:59:59';7.2 慢查询优化三板斧
处理慢查询的标准化流程:
- 确认问题SQL
-- 开启慢查询日志 SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1;分析执行计划(如7.1所示)
实施优化措施:
- 添加缺失索引
- 重写复杂查询
- 优化数据访问模式
8. 运维管理必备语句
8.1 数据库状态检查清单
日常巡检常用命令:
/* 查看运行状态 */ SHOW ENGINE INNODB STATUS; /* 查看连接数 */ SHOW STATUS LIKE 'Threads_connected'; /* 查看锁等待 */ SELECT * FROM performance_schema.events_waits_current; /* 查看表空间 */ SELECT table_name, ROUND(data_length/1024/1024,2) AS 'Size(MB)' FROM information_schema.tables WHERE table_schema = 'your_db';8.2 备份与恢复方案
逻辑备份的标准做法:
# 全量备份 mysqldump -uroot -p --single-transaction --master-data=2 db_name > backup.sql # 恢复数据 mysql -uroot -p db_name < backup.sql物理备份推荐方案:
# 使用Percona XtraBackup xtrabackup --backup --user=root --password=xxx --target-dir=/backup/ xtrabackup --prepare --target-dir=/backup/9. 安全管控关键语句
9.1 用户权限管理规范
最小权限原则实现示例:
-- 创建应用用户 CREATE USER 'app_user'@'192.168.1.%' IDENTIFIED BY 'complex_password'; -- 授予最小权限 GRANT SELECT, INSERT, UPDATE ON db_name.* TO 'app_user'@'192.168.1.%'; -- 查看用户权限 SHOW GRANTS FOR 'app_user'@'192.168.1.%';9.2 敏感数据保护方案
数据脱敏处理方法:
/* 存储时加密 */ INSERT INTO users (name, id_card) VALUES ('张三', AES_ENCRYPT('110101199003072316', 'encrypt_key')); /* 查询时解密 */ SELECT name, AES_DECRYPT(id_card, 'encrypt_key') AS id_card FROM users;审计日志配置:
-- 开启通用日志审计 SET GLOBAL general_log = ON; SET GLOBAL general_log_file = '/var/log/mysql/mysql-audit.log';10. 实战案例解析
10.1 电商分页查询优化
典型分页场景的性能陷阱与解决方案:
-- 低效写法(深度分页问题) SELECT * FROM products ORDER BY id LIMIT 1000000, 20; -- 优化方案1:基于主键查询 SELECT * FROM products WHERE id > 1000000 ORDER BY id LIMIT 20; -- 优化方案2:延迟关联 SELECT p.* FROM products p INNER JOIN (SELECT id FROM products ORDER BY id LIMIT 1000000, 20) AS tmp ON p.id = tmp.id;10.2 订单统计报表生成
复杂统计查询的优化思路:
-- 原始方案:多个子查询 SELECT (SELECT COUNT(*) FROM orders WHERE status=1) AS pending, (SELECT COUNT(*) FROM orders WHERE status=2) AS paid, (SELECT SUM(amount) FROM orders WHERE status=2) AS total_amount; -- 优化方案:条件聚合 SELECT COUNT(CASE WHEN status=1 THEN 1 END) AS pending, COUNT(CASE WHEN status=2 THEN 1 END) AS paid, SUM(CASE WHEN status=2 THEN amount ELSE 0 END) AS total_amount FROM orders;11. 版本特性速览
11.1 MySQL 8.0新特性应用
JSON处理增强:
-- JSON字段查询 SELECT * FROM products WHERE JSON_EXTRACT(specs, '$.weight') > 10; -- JSON数组展开 SELECT user_id, jt.order_id FROM users, JSON_TABLE(orders_json, '$[*]' COLUMNS ( order_id INT PATH '$.id' )) AS jt;11.2 5.7与8.0性能对比
常用操作性能测试(单位:QPS):
| 操作类型 | MySQL 5.7 | MySQL 8.0 | 提升幅度 |
|---|---|---|---|
| 点查询 | 12,500 | 15,200 | +21.6% |
| 范围查询 | 3,200 | 4,100 | +28.1% |
| 事务处理 | 850 | 1,100 | +29.4% |
| JSON操作 | 420 | 2,800 | +566% |
12. 常见错误排查手册
12.1 错误代码速查表
高频错误解决方案:
| 错误代码 | 含义 | 解决方案 |
|---|---|---|
| 1062 | 唯一键冲突 | 使用INSERT IGNORE或ON DUPLICATE KEY UPDATE |
| 1213 | 死锁 | 重试事务或调整事务顺序 |
| 1205 | 锁等待超时 | 优化事务时长或拆分大事务 |
| 2006 | 服务器连接断开 | 检查网络或增大wait_timeout |
12.2 性能问题诊断流程
系统变慢时的标准排查步骤:
- 检查服务器负载:top, vmstat
- 查看MySQL状态:SHOW PROCESSLIST
- 分析慢查询日志
- 检查锁等待情况
- 评估索引有效性
13. 实用工具函数集锦
13.1 日期处理大全
常见日期操作示例:
/* 日期格式化 */ SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); /* 日期计算 */ SELECT DATE_ADD(NOW(), INTERVAL 1 DAY) AS tomorrow, LAST_DAY(NOW()) AS month_last_day; /* 工作日计算 */ SELECT COUNT(*) AS work_days FROM calendar WHERE is_holiday = 0 AND date BETWEEN '2023-01-01' AND '2023-01-31';13.2 字符串处理技巧
高效字符串操作:
/* 智能分割 */ SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2) AS domain; /* JSON字符串提取 */ SELECT REGEXP_SUBSTR('{"id":101,"name":"张三"}', '"name":"([^"]+)"', 1, 1, '', 1) AS user_name; /* 相似度匹配 */ SELECT 'MySQL' SOUNDS LIKE 'My S Q L'; -- 返回114. 系统变量调优指南
14.1 关键参数配置建议
生产环境推荐配置:
[mysqld] # 内存配置 innodb_buffer_pool_size = 12G # 总内存的50-70% innodb_buffer_pool_instances = 8 # 日志配置 sync_binlog = 1 innodb_flush_log_at_trx_commit = 1 # 连接管理 max_connections = 500 thread_cache_size = 50 # 查询优化 query_cache_type = 0 # 8.0已移除 optimizer_switch = 'index_merge=off'14.2 状态监控指标
需要重点关注的性能指标:
-- 缓存命中率 SELECT (1 - (SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads') / (SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_read_requests')) AS hit_rate; -- 连接使用率 SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections';15. 数据迁移方案
15.1 同构数据库迁移
使用mysqldump的可靠方案:
# 导出结构+数据 mysqldump -uroot -p --single-transaction --routines --triggers db_name > dump.sql # 导入目标库 mysql -h new_host -u root -p db_name < dump.sql15.2 异构数据迁移
从CSV导入的高效方法:
-- 准备目标表 CREATE TABLE import_data (...); -- 执行导入 LOAD DATA INFILE '/path/to/data.csv' INTO TABLE import_data FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 ROWS;16. 高可用架构支持
16.1 主从复制配置
设置复制的���准步骤:
-- 主库配置 GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%' IDENTIFIED BY 'password'; FLUSH PRIVILEGES; -- 从库配置 CHANGE MASTER TO MASTER_HOST='master_host', MASTER_USER='repl_user', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154; START SLAVE;16.2 读写分离实现
应用层实现方案:
// Spring Boot配置示例 @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } @Bean public DataSource routingDataSource() { AbstractRoutingDataSource ds = new AbstractRoutingDataSource() { @Override protected Object determineCurrentLookupKey() { return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "slave" : "master"; } }; ds.setDefaultTargetDataSource(masterDataSource()); ds.setTargetDataSources(Map.of( "master", masterDataSource(), "slave", slaveDataSource() )); return ds; }17. 监控与报警方案
17.1 Prometheus监控配置
关键指标采集配置:
# mysqld_exporter配置示例 scrape_configs: - job_name: 'mysql' static_configs: - targets: ['mysql_host:9104'] metrics_path: /metrics params: collect[]: - global_status - innodb_metrics - performance_schema17.2 报警规则示例
关键报警规则配置:
groups: - name: mysql_alerts rules: - alert: MySQLDown expr: up{job="mysql"} == 0 for: 1m labels: severity: critical annotations: summary: "MySQL instance down" - alert: HighThreadsRunning expr: mysql_global_status_threads_running > 100 for: 5m labels: severity: warning annotations: summary: "High number of running threads"18. 云数据库优化
18.1 RDS参数调优
云数据库特有配置建议:
-- 设置秒级监控 CALL mysql.rds_set_configuration('performance_schema', '1'); -- 调整备份窗口 CALL mysql.rds_set_configuration('backup-retention-period', '7'); CALL mysql.rds_set_configuration('backup-window', '02:00-03:00');18.2 只读实例利用
读写分离实现方案:
-- 创建只读实例(控制台操作) -- 应用层配置读流量路由19. 数据归档策略
19.1 历史数据归档方案
基于分区的归档方案:
-- 创建分区表 CREATE TABLE orders ( id BIGINT, order_time DATETIME ) PARTITION BY RANGE (YEAR(order_time)) ( PARTITION p2020 VALUES LESS THAN (2021), PARTITION p2021 VALUES LESS THAN (2022), PARTITION p2022 VALUES LESS THAN (2023), PARTITION pmax VALUES LESS THAN MAXVALUE ); -- 归档旧数据 ALTER TABLE orders DROP PARTITION p2020;19.2 冷热数据分离
使用触发器自动归档:
CREATE TRIGGER archive_old_orders AFTER INSERT ON orders FOR EACH ROW BEGIN IF NEW.create_time < DATE_SUB(NOW(), INTERVAL 1 YEAR) THEN INSERT INTO orders_archive VALUES (NEW.*); DELETE FROM orders WHERE id = NEW.id; END IF; END;20. 扩展功能应用
20.1 GIS地理数据处理
空间数据查询示例:
-- 创建空间表 CREATE TABLE locations ( id INT PRIMARY KEY, name VARCHAR(100), position POINT NOT NULL SRID 4326, SPATIAL INDEX(position) ); -- 范围查询 SELECT id, name, ST_Distance_Sphere(position, POINT(116.404, 39.915)) AS distance FROM locations WHERE ST_Contains( ST_Buffer(POINT(116.404, 39.915), 1000), position ) ORDER BY distance;20.2 全文检索实现
中文全文检索配置:
-- 创建全文索引 ALTER TABLE articles ADD FULLTEXT INDEX ft_index (title, content) WITH PARSER ngram; -- 执行检索 SELECT id, title, MATCH(title, content) AGAINST('数据库 优化' IN NATURAL LANGUAGE MODE) AS score FROM articles WHERE MATCH(title, content) AGAINST('数据库 优化' IN NATURAL LANGUAGE MODE) ORDER BY score DESC;