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

。。。持续更新。。。

相关推荐
这里有鱼汤6 分钟前
80%新手炒股都在误用技术指标?一文揭秘正确分类与实战组合
后端·python
挖坑的张师傅12 分钟前
基于 Rust 的高性能 S3 over NFS 系统设计
后端·架构
澡点睡觉28 分钟前
【golang长途旅行第32站】反射
开发语言·后端·golang
用户61204149221328 分钟前
C语言做的排队叫号系统
c语言·后端·敏捷开发
IT_陈寒1 小时前
3年Java开发经验总结:提升50%编码效率的7个核心技巧与实战案例
前端·人工智能·后端
Victor3562 小时前
Redis(26)Redis的AOF持久化的优点和缺点是什么?
后端
Victor3562 小时前
Redis(27)如何对Redis进行备份和恢复?
后端
你的人类朋友9 小时前
【操作系统】Unix和Linux是什么关系?
后端·操作系统·unix
uzong10 小时前
半小时打造七夕传统文化网站:Qoder AI编程实战记录
后端·ai编程
快乐就是哈哈哈10 小时前
从传统遍历到函数式编程:彻底掌握 Java Stream 流
后端