告别21条军规建立基于执行引擎原理的高性能查询编写体系前菜为什么“优化清单”正在失效不知道你有没有这样的经历收藏了无数篇“SQL优化21条军规”、“SQL必知必会50条”面试前背得滚瓜烂熟可一到真实的生产环境系统依然卡顿查询依然超时。这不是你的错。是那些看似权威的“规则清单”正在集体失效。原因有三1. 数据库引擎已进化但你的知识还停留在5年前MySQL 8.0的哈希连接、窗口函数、通用表表达式PostgreSQL 14的并行查询优化、JIT编译……这些现代数据库特性让很多“古老”的优化规则如“永远不要用OR”变得过时甚至错误。2. 硬件与架构变革改变了性能瓶颈的位置当你的数据全部跑在NVMe SSD上当内存从32G升级到256G当数据库从单机迁移到云上的读写分离集群时性能瓶颈早已从“磁盘IO”转移到“网络延迟”、“锁竞争”和“执行计划选择”。3. 业务复杂度指数级增长简单规则无法应对微服务拆出上百张表实时大屏需要秒级响应A/B测试需要多维度聚合……这种场景下背诵“不要用SELECT *”这样的规则就像拿着勺子去救火。今天我们不谈“军规”我们来建立一套基于数据库执行引擎原理的现代SQL性能思维。这不仅是2026年高效工作的必需更是你超越80%只会背规则的工程师的关键。正菜构建你的SQL性能分析框架2026版第一章理解“成本”——优化器的视角核心认知数据库优化器不是魔法师它是一个精打细算的“成本会计师”。它的唯一目标是用最低的“成本”完成你的查询。什么是成本 CPU计算成本 内存使用成本 磁盘I/O成本 网络传输成本2026年的正确打开方式深度解读执行计划-- MySQL 8.0 EXPLAIN ANALYZE SELECT u.name, o.order_amount, p.product_name FROM users u JOIN orders o ON u.id o.user_id JOIN products p ON o.product_id p.id WHERE u.create_time 2026-01-01 ORDER BY o.order_time DESC LIMIT 100; -- PostgreSQL 14 EXPLAIN (ANALYZE, BUFFERS, VERBOSE) -- 同上查询看什么怎么看rowsvsactual rows优化器预估扫描100行实际扫描了10万行说明统计信息过时该更新了。Buffers共享块命中率低说明缺少合适的索引或内存设置不合理。执行时间分布时间都花在排序上考虑为ORDER BY子句增加索引。节点类型看到Nested Loop连接百万级大表大概率是连接条件没索引或优化器选错计划。关键动作每次性能调优前先用EXPLAIN ANALYZE拿到真实执行数据。优化不是“我觉得”而是“数据告诉我”。第二章减少“工作量”——查询编写优化原则让数据库做最少的工作。核心是减少扫描的数据量和减少计算复杂度。1. 关于SELECT *的真相过时的说法“不要用SELECT *因为网络传输大”。2026年的真相网络传输在现代内网环境下10G/25G通常不是瓶颈。真正的杀手是破坏覆盖索引机会导致不必要的回表。-- 假设有索引 idx_user_status (user_id, status) SELECT * FROM orders WHERE user_id 1000 AND status paid; -- 需要回表取所有字段 SELECT user_id, order_time, amount FROM orders WHERE user_id 1000 AND status paid; -- 如果创建索引 idx_user_status_time_amount (user_id, status, order_time, amount) -- 这就是覆盖索引性能可能有10倍以上差异2. 模糊查询的现代解决方案老生常谈“LIKE %xxx 不会走索引”。2026年的解决方案-- 方案A如果前缀固定用右模糊 SELECT * FROM products WHERE sku LIKE IPHONE16%; -- 方案B如果前缀不固定但业务允许 SELECT * FROM products WHERE REVERSE(sku) LIKE REVERSE(%PRO); -- 在sku_reverse字段上建立索引 -- 方案C复杂搜索场景用全文索引真正的降维打击 -- MySQL ALTER TABLE products ADD FULLTEXT INDEX idx_ft_desc (description); SELECT * FROM products WHERE MATCH(description) AGAINST(高性能 笔记本电脑 IN BOOLEAN MODE); -- PostgreSQL更强大 CREATE INDEX idx_gin_desc ON products USING GIN(to_tsvector(english, description)); SELECT * FROM products WHERE to_tsvector(english, description) to_tsquery(高性能 笔记本);3. 用EXISTS还是IN看数据分布新认知这取决于子查询结果的大小和NULL值的处理。-- 当子查询结果集小且关联列有索引时IN可能被优化为半连接 SELECT * FROM users WHERE id IN (SELECT user_id FROM vip_users); -- 当子查询结果集大或需要处理NULL时EXISTS通常更优 SELECT * FROM orders o WHERE EXISTS ( SELECT 1 FROM payments p WHERE p.order_id o.id AND p.status completed AND p.amount o.amount * 0.9 ); -- 2026年的新选择LATERAL JOINPostgreSQL/MySQL 8.0.14 SELECT u.*, latest_order.* FROM users u CROSS JOIN LATERAL ( SELECT * FROM orders WHERE user_id u.id ORDER BY created_at DESC LIMIT 1 ) latest_order;第三章设计“高速公路”——索引设计与优化黄金法则索引不是越多越好而是越准越好。每个索引都应该是为具体查询场景量身定制的“专用车道”。1. 复合索引设计不只是最左前缀进阶认知复合索引的顺序 高筛选度等值条件 范围条件/排序字段 覆盖字段-- 业务场景查询某个部门、某个时间段、某个状态的订单按金额排序 -- 错误设计 CREATE INDEX idx_orders ON orders(dept_id, status, order_time, amount); -- 正确设计2026年版本 CREATE INDEX idx_orders_optimized ON orders(dept_id, order_time, status, amount); -- 为什么dept_id是等值筛选order_time是范围查询放前面可以快速定位时间范围 -- status作为过滤条件amount用于排序和覆盖 -- 更高级为热查询创建专用索引 CREATE INDEX idx_hot_query ON orders(dept_id, order_time DESC) INCLUDE (amount, status) -- MySQL 8.0支持PostgreSQL长期支持 WHERE status IN (pending, processing); -- 部分索引PostgreSQL专属大招2. 函数索引解决“WHERE DATE(create_time)”难题-- 老问题这个查询用不上create_time的索引 SELECT * FROM logs WHERE DATE(create_time) 2026-05-19; -- 2026年解决方案函数索引 -- MySQL 8.0 CREATE INDEX idx_logs_created_date ON logs((DATE(create_time))); -- 更优雅的写法范围查询利用索引 SELECT * FROM logs WHERE create_time 2026-05-19 00:00:00 AND create_time 2026-05-20 00:00:00;3. 索引跳跃扫描理解现代优化器的黑科技MySQL 8.0.13和PostgreSQL都支持了某种形式的索引跳跃扫描。-- 假设有索引 idx_gender_city_age (gender, city, age) -- 老认知WHERE city北京 AND age30 用不上这个索引 -- 新现实现代优化器可能进行Index Skip Scan -- 它会先扫描gender的所有可能值M,F然后在每个值内使用索引的剩余部分 SELECT * FROM users WHERE city北京 AND age30; -- 执行计划中可能出现Index Skip Scan第四章规划“交通流”——事务、锁与并发控制核心洞察在高并发系统中锁竞争往往比慢查询更致命。1. LIMIT分批不只是防止误操作-- 危险操作一次性删除1000万条数据 DELETE FROM user_logs WHERE created_at 2020-01-01; -- 2026年安全操作分批删除控制事务大小 DELETE FROM user_logs WHERE created_at 2020-01-01 LIMIT 1000 RETURNING id; -- PostgreSQL支持MySQL可改为SELECT ... 后删除 -- 在应用程序中循环执行每次提交后暂停100ms -- 这避免了长事务、主从延迟暴涨、锁等待超时2. 监控锁竞争从被动等待到主动发现-- MySQL 8.0 锁监控 SELECT * FROM performance_schema.data_locks; SELECT * FROM sys.innodb_lock_waits; -- PostgreSQL 锁监控 SELECT pid, age(clock_timestamp(), query_start), usename, query, wait_event_type, wait_event FROM pg_stat_activity WHERE state active AND wait_event_type IS NOT NULL; -- 发现锁等待链 SELECT blockingl.relation::regclass, blocked_activity.pid AS blocked_pid, blocked_activity.query AS blocked_query, blocking_activity.pid AS blocking_pid, blocking_activity.query AS blocking_query FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid ! blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid blocking_locks.pid WHERE NOT blocked_locks.granted;3. 乐观锁高并发更新的优雅解决方案-- 悲观锁写法容易导致死锁 BEGIN; SELECT * FROM products WHERE id 1001 FOR UPDATE; -- 检查库存 UPDATE products SET stock stock - 1 WHERE id 1001; COMMIT; -- 乐观锁写法2026年推荐 UPDATE products SET stock stock - 1, version version 1 WHERE id 1001 AND stock 1 AND version 5; -- 传入应用程序中读取到的version -- 检查影响行数 IF ROW_COUNT() 0 THEN -- 更新失败被别人抢先了重新尝试或提示用户 END IF;第五章利用“新武器”——现代SQL语法与特性1. 窗口函数一句SQL完成复杂分析-- 老方法需要多次查询或应用程序循环 -- 新方法一句SQL搞定 SELECT user_id, order_date, amount, -- 计算每个用户的累计消费 SUM(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS running_total, -- 计算每个用户消费排名 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY amount DESC) AS order_rank, -- 计算与上次消费的间隔天数 order_date - LAG(order_date) OVER (PARTITION BY user_id ORDER BY order_date) AS days_since_last_order FROM orders WHERE order_date 2026-01-01;2. 通用表表达式让复杂查询清晰易懂-- 查询每个部门销售额前三的员工及其贡献占比 WITH department_sales AS ( SELECT dept_id, employee_id, SUM(amount) AS total_sales FROM orders WHERE order_date 2026-01-01 GROUP BY dept_id, employee_id ), ranked_employees AS ( SELECT dept_id, employee_id, total_sales, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY total_sales DESC) AS sales_rank FROM department_sales ), department_totals AS ( SELECT dept_id, SUM(total_sales) AS dept_total FROM department_sales GROUP BY dept_id ) SELECT r.dept_id, r.employee_id, r.total_sales, d.dept_total, ROUND(r.total_sales * 100.0 / d.dept_total, 2) AS contribution_percent FROM ranked_employees r JOIN department_totals d ON r.dept_id d.dept_id WHERE r.sales_rank 3 ORDER BY r.dept_id, r.sales_rank;3. JSONB查询优化结构化与非结构化的完美结合-- PostgreSQL JSONB 示例 -- 创建支持JSONB字段多条件查询的GIN索引 CREATE INDEX idx_products_attrs ON products USING GIN(attributes); -- 复杂查询属性中颜色为红色或蓝色且价格在100-200之间且评价数100的商品 SELECT * FROM products WHERE attributes {color: red} -- 包含红色 OR attributes {color: blue} -- 或包含蓝色 AND (attributes-price)::numeric BETWEEN 100 AND 200 AND (attributes-review_count)::integer 100 AND attributes ? in_stock; -- 包含in_stock键餐后甜点一句话性能清单2026速记版先看计划再动手永远先用EXPLAIN ANALYZE看执行计划索引为查询而生每个索引都应有具体的查询场景让查询只走索引尽可能使用覆盖索引避免回表复合索引顺序 高筛选度等值条件 范围/排序字段 覆盖字段小表驱动大表在JOIN时让结果集小的表做驱动表事务要短锁要轻批量操作用LIMIT分批高并发更新用乐观锁了解你的数据定期更新统计信息特别是分布不均匀的字段字符串处理是性能杀手避免在WHERE中对字段做函数计算批量操作优于循环一条多值INSERT比多条单值INSERT快得多合适的技术做合适的事复杂分析用窗口函数全文搜索用倒排索引写在最后在2026年SQL优化不再是背诵规则而是理解数据库如何思考并帮助它做出更好决策的能力。最影响性能的往往不是一两个慢查询而是错误的数据模型设计。在考虑优化之前先问自己表结构合理吗关系设计正确吗数据类型合适吗你的经验在实际工作中哪一条过时的“SQL军规”给你带来的困扰最大或者你有哪些基于现代数据库MySQL 8PostgreSQL 14的独家优化心得欢迎在评论区分享你的实战案例和踩坑经历让我们共同构建面向未来的SQL性能优化知识库。
2026年SQL性能优化实战:从“规则背诵”到“原理驱动”的思维跃迁
发布时间:2026/5/21 3:42:27
告别21条军规建立基于执行引擎原理的高性能查询编写体系前菜为什么“优化清单”正在失效不知道你有没有这样的经历收藏了无数篇“SQL优化21条军规”、“SQL必知必会50条”面试前背得滚瓜烂熟可一到真实的生产环境系统依然卡顿查询依然超时。这不是你的错。是那些看似权威的“规则清单”正在集体失效。原因有三1. 数据库引擎已进化但你的知识还停留在5年前MySQL 8.0的哈希连接、窗口函数、通用表表达式PostgreSQL 14的并行查询优化、JIT编译……这些现代数据库特性让很多“古老”的优化规则如“永远不要用OR”变得过时甚至错误。2. 硬件与架构变革改变了性能瓶颈的位置当你的数据全部跑在NVMe SSD上当内存从32G升级到256G当数据库从单机迁移到云上的读写分离集群时性能瓶颈早已从“磁盘IO”转移到“网络延迟”、“锁竞争”和“执行计划选择”。3. 业务复杂度指数级增长简单规则无法应对微服务拆出上百张表实时大屏需要秒级响应A/B测试需要多维度聚合……这种场景下背诵“不要用SELECT *”这样的规则就像拿着勺子去救火。今天我们不谈“军规”我们来建立一套基于数据库执行引擎原理的现代SQL性能思维。这不仅是2026年高效工作的必需更是你超越80%只会背规则的工程师的关键。正菜构建你的SQL性能分析框架2026版第一章理解“成本”——优化器的视角核心认知数据库优化器不是魔法师它是一个精打细算的“成本会计师”。它的唯一目标是用最低的“成本”完成你的查询。什么是成本 CPU计算成本 内存使用成本 磁盘I/O成本 网络传输成本2026年的正确打开方式深度解读执行计划-- MySQL 8.0 EXPLAIN ANALYZE SELECT u.name, o.order_amount, p.product_name FROM users u JOIN orders o ON u.id o.user_id JOIN products p ON o.product_id p.id WHERE u.create_time 2026-01-01 ORDER BY o.order_time DESC LIMIT 100; -- PostgreSQL 14 EXPLAIN (ANALYZE, BUFFERS, VERBOSE) -- 同上查询看什么怎么看rowsvsactual rows优化器预估扫描100行实际扫描了10万行说明统计信息过时该更新了。Buffers共享块命中率低说明缺少合适的索引或内存设置不合理。执行时间分布时间都花在排序上考虑为ORDER BY子句增加索引。节点类型看到Nested Loop连接百万级大表大概率是连接条件没索引或优化器选错计划。关键动作每次性能调优前先用EXPLAIN ANALYZE拿到真实执行数据。优化不是“我觉得”而是“数据告诉我”。第二章减少“工作量”——查询编写优化原则让数据库做最少的工作。核心是减少扫描的数据量和减少计算复杂度。1. 关于SELECT *的真相过时的说法“不要用SELECT *因为网络传输大”。2026年的真相网络传输在现代内网环境下10G/25G通常不是瓶颈。真正的杀手是破坏覆盖索引机会导致不必要的回表。-- 假设有索引 idx_user_status (user_id, status) SELECT * FROM orders WHERE user_id 1000 AND status paid; -- 需要回表取所有字段 SELECT user_id, order_time, amount FROM orders WHERE user_id 1000 AND status paid; -- 如果创建索引 idx_user_status_time_amount (user_id, status, order_time, amount) -- 这就是覆盖索引性能可能有10倍以上差异2. 模糊查询的现代解决方案老生常谈“LIKE %xxx 不会走索引”。2026年的解决方案-- 方案A如果前缀固定用右模糊 SELECT * FROM products WHERE sku LIKE IPHONE16%; -- 方案B如果前缀不固定但业务允许 SELECT * FROM products WHERE REVERSE(sku) LIKE REVERSE(%PRO); -- 在sku_reverse字段上建立索引 -- 方案C复杂搜索场景用全文索引真正的降维打击 -- MySQL ALTER TABLE products ADD FULLTEXT INDEX idx_ft_desc (description); SELECT * FROM products WHERE MATCH(description) AGAINST(高性能 笔记本电脑 IN BOOLEAN MODE); -- PostgreSQL更强大 CREATE INDEX idx_gin_desc ON products USING GIN(to_tsvector(english, description)); SELECT * FROM products WHERE to_tsvector(english, description) to_tsquery(高性能 笔记本);3. 用EXISTS还是IN看数据分布新认知这取决于子查询结果的大小和NULL值的处理。-- 当子查询结果集小且关联列有索引时IN可能被优化为半连接 SELECT * FROM users WHERE id IN (SELECT user_id FROM vip_users); -- 当子查询结果集大或需要处理NULL时EXISTS通常更优 SELECT * FROM orders o WHERE EXISTS ( SELECT 1 FROM payments p WHERE p.order_id o.id AND p.status completed AND p.amount o.amount * 0.9 ); -- 2026年的新选择LATERAL JOINPostgreSQL/MySQL 8.0.14 SELECT u.*, latest_order.* FROM users u CROSS JOIN LATERAL ( SELECT * FROM orders WHERE user_id u.id ORDER BY created_at DESC LIMIT 1 ) latest_order;第三章设计“高速公路”——索引设计与优化黄金法则索引不是越多越好而是越准越好。每个索引都应该是为具体查询场景量身定制的“专用车道”。1. 复合索引设计不只是最左前缀进阶认知复合索引的顺序 高筛选度等值条件 范围条件/排序字段 覆盖字段-- 业务场景查询某个部门、某个时间段、某个状态的订单按金额排序 -- 错误设计 CREATE INDEX idx_orders ON orders(dept_id, status, order_time, amount); -- 正确设计2026年版本 CREATE INDEX idx_orders_optimized ON orders(dept_id, order_time, status, amount); -- 为什么dept_id是等值筛选order_time是范围查询放前面可以快速定位时间范围 -- status作为过滤条件amount用于排序和覆盖 -- 更高级为热查询创建专用索引 CREATE INDEX idx_hot_query ON orders(dept_id, order_time DESC) INCLUDE (amount, status) -- MySQL 8.0支持PostgreSQL长期支持 WHERE status IN (pending, processing); -- 部分索引PostgreSQL专属大招2. 函数索引解决“WHERE DATE(create_time)”难题-- 老问题这个查询用不上create_time的索引 SELECT * FROM logs WHERE DATE(create_time) 2026-05-19; -- 2026年解决方案函数索引 -- MySQL 8.0 CREATE INDEX idx_logs_created_date ON logs((DATE(create_time))); -- 更优雅的写法范围查询利用索引 SELECT * FROM logs WHERE create_time 2026-05-19 00:00:00 AND create_time 2026-05-20 00:00:00;3. 索引跳跃扫描理解现代优化器的黑科技MySQL 8.0.13和PostgreSQL都支持了某种形式的索引跳跃扫描。-- 假设有索引 idx_gender_city_age (gender, city, age) -- 老认知WHERE city北京 AND age30 用不上这个索引 -- 新现实现代优化器可能进行Index Skip Scan -- 它会先扫描gender的所有可能值M,F然后在每个值内使用索引的剩余部分 SELECT * FROM users WHERE city北京 AND age30; -- 执行计划中可能出现Index Skip Scan第四章规划“交通流”——事务、锁与并发控制核心洞察在高并发系统中锁竞争往往比慢查询更致命。1. LIMIT分批不只是防止误操作-- 危险操作一次性删除1000万条数据 DELETE FROM user_logs WHERE created_at 2020-01-01; -- 2026年安全操作分批删除控制事务大小 DELETE FROM user_logs WHERE created_at 2020-01-01 LIMIT 1000 RETURNING id; -- PostgreSQL支持MySQL可改为SELECT ... 后删除 -- 在应用程序中循环执行每次提交后暂停100ms -- 这避免了长事务、主从延迟暴涨、锁等待超时2. 监控锁竞争从被动等待到主动发现-- MySQL 8.0 锁监控 SELECT * FROM performance_schema.data_locks; SELECT * FROM sys.innodb_lock_waits; -- PostgreSQL 锁监控 SELECT pid, age(clock_timestamp(), query_start), usename, query, wait_event_type, wait_event FROM pg_stat_activity WHERE state active AND wait_event_type IS NOT NULL; -- 发现锁等待链 SELECT blockingl.relation::regclass, blocked_activity.pid AS blocked_pid, blocked_activity.query AS blocked_query, blocking_activity.pid AS blocking_pid, blocking_activity.query AS blocking_query FROM pg_catalog.pg_locks blocked_locks JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid blocked_locks.pid JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype blocked_locks.locktype AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid AND blocking_locks.pid ! blocked_locks.pid JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid blocking_locks.pid WHERE NOT blocked_locks.granted;3. 乐观锁高并发更新的优雅解决方案-- 悲观锁写法容易导致死锁 BEGIN; SELECT * FROM products WHERE id 1001 FOR UPDATE; -- 检查库存 UPDATE products SET stock stock - 1 WHERE id 1001; COMMIT; -- 乐观锁写法2026年推荐 UPDATE products SET stock stock - 1, version version 1 WHERE id 1001 AND stock 1 AND version 5; -- 传入应用程序中读取到的version -- 检查影响行数 IF ROW_COUNT() 0 THEN -- 更新失败被别人抢先了重新尝试或提示用户 END IF;第五章利用“新武器”——现代SQL语法与特性1. 窗口函数一句SQL完成复杂分析-- 老方法需要多次查询或应用程序循环 -- 新方法一句SQL搞定 SELECT user_id, order_date, amount, -- 计算每个用户的累计消费 SUM(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS running_total, -- 计算每个用户消费排名 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY amount DESC) AS order_rank, -- 计算与上次消费的间隔天数 order_date - LAG(order_date) OVER (PARTITION BY user_id ORDER BY order_date) AS days_since_last_order FROM orders WHERE order_date 2026-01-01;2. 通用表表达式让复杂查询清晰易懂-- 查询每个部门销售额前三的员工及其贡献占比 WITH department_sales AS ( SELECT dept_id, employee_id, SUM(amount) AS total_sales FROM orders WHERE order_date 2026-01-01 GROUP BY dept_id, employee_id ), ranked_employees AS ( SELECT dept_id, employee_id, total_sales, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY total_sales DESC) AS sales_rank FROM department_sales ), department_totals AS ( SELECT dept_id, SUM(total_sales) AS dept_total FROM department_sales GROUP BY dept_id ) SELECT r.dept_id, r.employee_id, r.total_sales, d.dept_total, ROUND(r.total_sales * 100.0 / d.dept_total, 2) AS contribution_percent FROM ranked_employees r JOIN department_totals d ON r.dept_id d.dept_id WHERE r.sales_rank 3 ORDER BY r.dept_id, r.sales_rank;3. JSONB查询优化结构化与非结构化的完美结合-- PostgreSQL JSONB 示例 -- 创建支持JSONB字段多条件查询的GIN索引 CREATE INDEX idx_products_attrs ON products USING GIN(attributes); -- 复杂查询属性中颜色为红色或蓝色且价格在100-200之间且评价数100的商品 SELECT * FROM products WHERE attributes {color: red} -- 包含红色 OR attributes {color: blue} -- 或包含蓝色 AND (attributes-price)::numeric BETWEEN 100 AND 200 AND (attributes-review_count)::integer 100 AND attributes ? in_stock; -- 包含in_stock键餐后甜点一句话性能清单2026速记版先看计划再动手永远先用EXPLAIN ANALYZE看执行计划索引为查询而生每个索引都应有具体的查询场景让查询只走索引尽可能使用覆盖索引避免回表复合索引顺序 高筛选度等值条件 范围/排序字段 覆盖字段小表驱动大表在JOIN时让结果集小的表做驱动表事务要短锁要轻批量操作用LIMIT分批高并发更新用乐观锁了解你的数据定期更新统计信息特别是分布不均匀的字段字符串处理是性能杀手避免在WHERE中对字段做函数计算批量操作优于循环一条多值INSERT比多条单值INSERT快得多合适的技术做合适的事复杂分析用窗口函数全文搜索用倒排索引写在最后在2026年SQL优化不再是背诵规则而是理解数据库如何思考并帮助它做出更好决策的能力。最影响性能的往往不是一两个慢查询而是错误的数据模型设计。在考虑优化之前先问自己表结构合理吗关系设计正确吗数据类型合适吗你的经验在实际工作中哪一条过时的“SQL军规”给你带来的困扰最大或者你有哪些基于现代数据库MySQL 8PostgreSQL 14的独家优化心得欢迎在评论区分享你的实战案例和踩坑经历让我们共同构建面向未来的SQL性能优化知识库。