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

。。。持续更新。。。

相关推荐
用户48221371677522 分钟前
C++——复合数据类型(数组、字符串)
后端
用户9037001671526 分钟前
分布式阻塞式限流学习及分享
后端
熊猫片沃子31 分钟前
Mybatis中进行批量修改的方法
java·后端·mybatis
养鱼的程序员33 分钟前
零基础搭建个人网站:从 Astro 框架到 GitHub 自动部署完全指南
前端·后端·github
白应穷奇42 分钟前
编写高性能数据处理代码 01
后端·python
杨充1 小时前
03.接口vs抽象类比较
前端·后端
一只叫煤球的猫1 小时前
基于Redisson的高性能延迟队列架构设计与实现
java·redis·后端
卡尓1 小时前
使用 Layui 替换 Yii 基础模板的默认 Bootstrap 样式并尝试重写导航栏组件
后端
WhyWhatHow1 小时前
JEnv:新一代Java环境管理器,让多版本Java管理变得简单高效
java·后端
Rust语言中文社区1 小时前
Rust 训练营二期来袭: Rust + AI 智能硬件
开发语言·后端·rust