美文网首页
SQL查询技巧分析

SQL查询技巧分析

作者: 右耳菌 | 来源:发表于2022-09-27 17:33 被阅读0次

    1. Like 会不会走索引?

    在‘%’在后边的情况下会走索引,即前面非通配符的情况下是会走索引的,此时的本质是一个范围查询,即使用了前面的内容进行了字符大小对比。
    在‘%’在前边的情况下不会走索引。此时无法使用字符进行大小对比。

    -- 走索引
    explain select * from myshop.ecs_users where email like 'onlyoneemail.com'; -- 本质是一个范围查询
    -- 走索引
    explain select * from myshop.ecs_users where email like 'onlyoneemail.com%'; -- 本质是一个范围查询
    -- 不走索引
    explain select * from myshop.ecs_users where email like '%onlyoneemail.com';
    -- 不走索引
    explain select * from myshop.ecs_users where email like '%onlyoneemail.com%';
    

    2. 索引列能不能为空?

    IS NULL 会走索引,IS NOT NULL 不会走索引,但是索引列是可以为空的,且使用is null 查询的时候,是走索引的。

    -- 走索引
    explain select * from myshop.ecs_users where email is null;
    -- 不走索引
    explain select * from myshop.ecs_users where email is not null;
    

    3. 函数计算会不会走索引?

    此情况以执行计划为准,与sql查询语句的查询情况为准。

    -- 走索引,count(*) 实际是count了index的内容
    explain select count(*) from myshop.ecs_order_info where from_unixtime(add_time, '%Y-%m-%d') = '2022-09-27';
    -- 不走索引,此时index无法满足要求
    explain select * from myshop.ecs_order_info where from_unixtime(add_time, '%Y-%m-%d') = '2022-09-27';
    -- 走索引
    explain select count(*) from myshop.ecs_order_info where abs(add_time) > 159025358;
    

    4. 类型不一致会不会走索引?

    -- 走索引,这种情况会走
    explain select * from myshop.ecs_order_info where add_time >= 1591025358;
    

    使用下面的内容来分析

    select * from myshop.ecs_order_info where add_time >= 1591025358;
    
    set optimizer_trace = "enabled=on";
    select trace from information_schema.optimizer_trace;
    set optimizer_trace = "enabled=off";
    

    5. where 条件顺序怎么写?

    多列索引,顺序反了会不会走索引?

    -- 案例5–多列索引,顺序反了会不会走索引?索引idx_ship_pay(pay_id , shipping_id,pay_time)
    -- 记住最左前缀的概念
    -- 走索引
    explain SELECT* FROM myshop.ecs_order_info where pay_time = '1591025358' and shipping_id = 4 and pay_id = 2 ;
    -- 不走索引
    explain SELECT * FROM myshop.ecs_order_info where shipping_id = 2 and pay_time >= '1591025358';
    -- 不走索引
    explain SELECT * FROM myshop.ecs_order_info where shipping_id = 2;
    -- 走索引
    explain SELECT *FROM myshop.ecs_order_info where pay_id = 2 and pay_time >= '1591025358';
    
    -- 不用组合索引,查询多个单列索引会不会只有一个索引生效?–索引合并
    -- 走索引,此时会索引合并,即两个索引各走一边,然后合并
    explain select * from myshop.ecs_users where email = 'onlyoneemail.com' or user_name = 'edu_159100060138810';
    

    6. 要不要用UNION替代OR

    如果不嫌麻烦可以考虑替换,如果比较麻烦也可以不替换。

    -- query_cost: 约等于 5.02
    select*from myshop.ecs_users where user_name = 'edu_159100060138810' or email = 'onlyoneemail.com';
    
    -- query_cost: 约等于 2.4
    select * from myshop.ecs_users where email = 'onlyoneemail.com'
    union
    select * from myshop.ecs_users where user_name = 'edu_159100060138810';
    

    7. EXISTS vs IN

    -- 案例7 - EXISTS VS IN - 是否需要用EXISTS替代IN、用NOT EXISTS替代NOT IN?

    select * from myshop.ecs_users where user_id 
    in ( SELECT user_id FROM myshop.ecs_order_info where add_time >= 1590076800 ) limit 1;
    
    select * from myshop.ecs_users u where 
    EXISTS (SELECT user_id FROM myshop.ecs_order_info o where add_time >= 1590076800 and u.user_id = o.user_id)  limit 1;
    

    -- 常见情况下,子查询结果少,用in ,子查询结果多,用exists
    -- 根据实际运行情况进行分析

    -- 快
    select * from myshop.ecs_users where user_id in ( 1,2,4);
    -- 慢
    select * from myshop.ecs_users u where EXISTS 
    ( select * from (
    select 1 user_id union select 2 union select 4
    ) u1 where u.user_id = u1.user_id );
    

    8. 非等于会不会走索引?

    -- 案例8 - != 和 <> 会不会走索引?
    -- 走索引,因为是主键
    explain select * from myshop.ecs_users where user_id != 999;
    -- 不走索引,基于表数据的分布情况,极端情况下,如果只有一条是等于的,那么基本等于全表扫描。
    explain select * from myshop.ecs_users where user_name != 'edu_159100060138810';
    -- 走索引,count基本上都会走索引
    explain select count(*) from myshop.ecs_users where user_name != 'edu_159100060138810';
    

    这里可以使用show index from tableName 来查看索引的情况,如下:

    其中Cardinality越大,表示数据越分散。


    9. 索引覆盖

    -- 还需要再查询一次具体的数据

    SELECT * FROM myshop.ecs_users WHERE last_login_time >= 1591025358 limit 10000, 1000; 
    

    -- 这里可以直接从索引中拿到数据,就不用再去拿全部的数据了
    -- 所以查询的列如果在索引中的话,建议使用索引覆盖的方式

    SELECT user_id FROM myshop.ecs_users WHERE last_login_time >= 1591025358 limit 10000, 1000; 
    

    10. 用子查询还是表关联?

    -- 多表关联、子查询------------------------------------------
    -- 需求:假设今天6.1日,查询最近20天 总消费金额 高于 3W 的 且近7天登录过用户信息
    -- 注意: 第一次查询会非常慢,你的buffer_pool调大一点

    SELECT count(*) FROM myshop.ecs_order_info where add_time >= 1590076800; -- 100W
    SELECT count(*) FROM myshop.ecs_users where last_login_time >= 1590076800; -- 33W
    
    • 表关联的方式
    SELECT 
        u.user_id, SUM(o.money_paid) pay
    FROM
        myshop.ecs_order_info o, myshop.ecs_users u
    WHERE u.last_login_time >= 1590076800
    AND o.add_time >= 1590076800 
    AND o.user_id = u.user_id
    GROUP BY o.user_id
    HAVING pay > 30000;
    
    • 子查询的方式
    SELECT u.user_id, o.pay FROM
    myshop.ecs_users u, 
    (
        select user_id,SUM(money_paid) pay from myshop.ecs_order_info where add_time >= 1590076800 
        GROUP BY user_id HAVING pay > 30000
    )  o
    where o.user_id = u.user_id
    and u.last_login_time >= 1590076800;
    

    总结:建议使用多表的情况进行查询。


    11. 表关联之大表小表

    -- 大表关联小表,还是小表关联大表? -- 统计某个地区的订单

    explain select r.region_name,count(o.order_id) from myshop.ecs_region r , myshop.ecs_order_info o
    where r.region_id = o.province and o.add_time >= 1591025358  group by r.region_name;
    
    explain select r.region_name,count(o.order_id) from myshop.ecs_order_info o , myshop.ecs_region r 
    where r.region_id = o.province and o.add_time >= 1591025358  group by r.region_name;
    
    explain select r.region_name,count(o.order_id) from myshop.ecs_region r left join myshop.ecs_order_info o
    on r.region_id = o.province where o.add_time >= 1591025358  group by r.region_name;
    
    explain select r.region_name,count(o.order_id) from myshop.ecs_order_info o left join myshop.ecs_region r
    on r.region_id = o.province where o.add_time >= 1591025358  group by r.region_name;
    

    经过检验,其实无论是放大表在前还是小表在前,是没有关系的,本质上来说,其实mysql的其实会自己分析进行优化,所以无需关心。


    12. 分页的玩法

    -- 分页案例 -----------------------------------------
    -- 1. count(*) 解疑

    select count(email) from myshop.ecs_users;  -- 不统计null
    select count(1) from myshop.ecs_users; -- 不解析内容
    select count(*) from myshop.ecs_users; -- 标准SQL
    

    -- 2. 分页(查你需要的字段,不要*)

    select * from myshop.ecs_order_info order by order_id limit 1,100;
    -- 下面这个会变得很慢
    select * from myshop.ecs_order_info order by order_id limit 4000000,100;
    
    • 递增ID,连续不中断
    select * from myshop.ecs_order_info o where o.order_id between 4000000 and 4000100;
    
    • 递增ID, 不连续
    select * from myshop.ecs_order_info o where o.order_id 
    >=
    (select order_id from myshop.ecs_order_info order by order_id limit 4000000,1) limit 100;
    
    • 无序读取
    SELECT * FROM myshop.ecs_users u where u.last_login_time >= 1590076800 order by u.last_login_time,u.user_id limit 1, 10;
    
    SELECT * FROM myshop.ecs_users u where u.last_login_time >= 1590076800 order by u.last_login_time,u.user_id limit 200000, 10;
    
    SELECT * FROM myshop.ecs_users u , 
    ( 
        SELECT user_id FROM myshop.ecs_users u where u.last_login_time >= 1590076800 order by u.last_login_time,u.user_id limit 200000, 10 
    ) u1 where u1.user_id = u.user_id order by u.user_id;
    

    如果觉得有收获,欢迎点赞和评论,更多知识,请点击关注查看我的主页信息哦~

    相关文章

      网友评论

          本文标题:SQL查询技巧分析

          本文链接:https://www.haomeiwen.com/subject/awxfartx.html