代码优化-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;

。。。持续更新。。。

相关推荐
稚辉君.MCA_P8_Java6 分钟前
Gemini永久会员 containerd部署java项目 kubernetes集群
后端·spring cloud·云原生·容器·kubernetes
yihuiComeOn31 分钟前
[源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅
java·后端·spring
e***71671 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
程序猿小蒜2 小时前
基于springboot的的学生干部管理系统开发与设计
java·前端·spring boot·后端·spring
q***56382 小时前
Spring容器初始化扩展点:ApplicationContextInitializer
java·后端·spring
菜鸟‍2 小时前
【后端学习】MySQL数据库
数据库·后端·学习·mysql
Codebee3 小时前
30 分钟落地全栈交互:OneCode CLI+SVG 排课表实战
后端
TechTrek3 小时前
Spring Boot 4.0正式发布了
java·spring boot·后端·spring boot 4.0
飞梦工作室4 小时前
企业级 Spring Boot 邮件系统开发指南:从基础到高可用架构设计
java·spring boot·后端
haiyu柠檬4 小时前
在Spring Boot中实现Azure的SSO+VUE3前端配置
java·spring boot·后端