代码优化-SQL优化例子

是时候总结一下以前的SQL优化例子了😁哈哈哈哈,欢迎交流

注意:SQL含有伪代码

1、投影(只取所需要的字段)

号称最简单的SQL优化-投影,就是只返回需要的字段数据。

比如我想查询groupID和成员ID list,就可以直接查询group_id,user_id,减少查询返回的数据。

vbnet 复制代码
//实体的投影:group_member表字段很多,当我只需要查询 group的ID和group的成员ID时
@Data
public class GroupRelId implements Serializable {
    Long id;
    String userIds;
}
sql 复制代码
@Select(value = "SELECT group_id as id, GROUP_CONCAT(user_id) AS user_ids FROM ch_group_member WHERE group_id is not null GROUP BY group_id")
List<GroupRelId> findRelIdAll();
2、多个SQL 优化为单个SQL(适合级联查询的数据)

有时为了减少数据库的访问次数,会将几个简单的SQL 改成一条SQL去执行。 可能有人会说不要连表查询,阿里规约上禁止连表,我觉得应该根据业务需求去选择,而不是用金科玉律去束缚自己。

比如:查询群组Group的详情,group和member虽然是两个实体对象,但是他们的关系不仅仅是组合,而是聚合关系。而且一个group的members不会太大,顶多1000多个。

多次SQL查询,再组装数据:group.setMembers(members)

csharp 复制代码
-- 查询 group
select * from tb_group where id=#{groupId};
-- 查询 member list
select * from tb_group_member where group_id=#{groupId};

优化为 直接级联查询。

vbnet 复制代码
 SELECT g.*,
        m.id AS m_id,
        m.group_id AS m_group_id,
        m.user_id AS m_user_id,
        m.alias_name AS m_alias_name,
        m.nickname AS m_nickname,
        m.avatar_url AS m_avatar_url,
        m.gender AS m_gender
        FROM tb_group g
        LEFT JOIN tb_group_member m ON g.id = m.group_id
        WHERE g.group_id=#{groupId} )
3、复杂SQL 拆分为 简单SQL(将一条业务复杂的SQL操作拆分为几条简单的SQL)

有时为了降低业务的复杂程度,会将一条复杂的SQL拆分为几条简单的SQL去执行。

比如现在要分页查询商品goods+分类名+品牌名+销售属性list(因为前端页面要展示这些数据):

sql 复制代码
SELECT g.*, c.name AS cate_name, b.name AS brand_name, sa.* 
FROM 
    (SELECT * FROM tb_goods WHERE is_sale = TRUE LIMIT ${size} OFFSET ${offset}) g  --商品主表
LEFT JOIN tb_category c 
    ON g.category_id = c.id  -商品分类表
LEFT JOIN tb_brand b 
    ON g.brand_id = b.id  --商品品牌表
LEFT JOIN rel_goods_saleattr rs  --关联表
    ON g.id = rs.goods_id 
LEFT JOIN tb_sale_attribute sa  --商品销售属性表(一对多)
    ON rs.attr_id = sa.id;

拆分为多条SQL执行

step1------查询商品主表分页; step2------查询商品的销售属性list; 再组合并返回前端渲染

sql 复制代码
-- 查询商品主表分页
SELECT * FROM tb_goods WHERE is_sale = TRUE LIMIT ${size} OFFSET ${offset};

-- 查询销售属性list(但是这样会在分页内,循环查询SQL)
SELECT * FROM tb_sale_attribute sa --商品销售属性表(一对多)
INNER JOIN rel_goods_saleattr rs   --关联表
ON rs.attr_id = sa.id
WHERE rs.goods_id=#{goodsId} 

-- (优化为批量查询消息属性list,业务代码组装为map,再去goods.setSaleAttrs(map[goodsId]))
SELECT * FROM tb_sale_attribute sa --商品销售属性表(一对多)
INNER JOIN rel_goods_saleattr rs   --关联表
ON rs.attr_id = sa.id
WHERE rs.goods_id IN #{goodsIds} -- goodsIds为每次分页后的id list

进一步优化:由于品牌名称、分类名称几乎不会更改。避免每次连表查询。

可以冗余brandName、categoryName字段到goods表中(缺点是名称更改时,需要刷数据)。 页可以缓存到Redis中: id2brandName、id2categoryName(名称更改时,需更新缓存)。

4、使用窗口函数

比如要查询 每个分类下销量前100的商品

sql 复制代码
SELECT g1.*, cate.name AS category_name 
FROM serve_goods g1 
JOIN tb_category cate --关联分类表获取分类名
ON g1.cate_id = cate.id 
WHERE 
    ( SELECT COUNT(*) FROM serve_goods g2 
        WHERE g2.cate_id = g1.cate_id AND g2.sale_count > g1.sale_count 
    ) < 100 
    AND g1.is_sale = true 
ORDER BY g1.cate_id, g1.sale_count DESC;

优化后

sql 复制代码
-- 根据 category 分组且排序

WITH SortedGoods AS (
    SELECT sg.*, cate.name AS category_name,
        ROW_NUMBER() OVER (PARTITION BY sg.cate_id ORDER BY sg.sales_count DESC) as rn
    FROM serve_goods sg
    LEFT JOIN tb_category cate --关联分类表获取分类名
        ON sg.cate_id = cate.id 
    WHERE sg.is_sale = true 
)
SELECT * FROM SortedGoods WHERE rn<=#{limitSize} ORDER BY cate_id,rn;

。。。持续更新。。。

相关推荐
bearpping31 分钟前
SpringBoot最佳实践之 - 使用AOP记录操作日志
java·spring boot·后端
一叶飘零_sweeeet33 分钟前
线上故障零扩散:全链路监控、智能告警与应急响应 SOP 完整落地指南
java·后端·spring
开心就好20252 小时前
不同阶段的 iOS 应用混淆工具怎么组合使用,源码混淆、IPA混淆
后端·ios
架构师沉默2 小时前
程序员如何避免猝死?
java·后端·架构
椰奶燕麦2 小时前
Windows PackageManager (winget) 核心故障排错与通用修复指南
后端
zjjsctcdl2 小时前
springBoot发布https服务及调用
spring boot·后端·https
zdl6863 小时前
Spring Boot文件上传
java·spring boot·后端
世界哪有真情3 小时前
哇!绝了!原来这么简单!我的 Java 项目代码终于被 “拯救” 了!
java·后端
RMB Player3 小时前
Spring Boot 集成飞书推送超详细教程:文本消息、签名校验、封装工具类一篇搞定
java·网络·spring boot·后端·spring·飞书
重庆小透明3 小时前
【搞定面试之mysql】第三篇 mysql的锁
java·后端·mysql·面试·职场和发展