下列优化的SQL案例,区别于平常加SQL索引的方法优化,大部分都是通过改写SQL语句方法优化,都是日常优化线上慢SQL的实际案例,有比较好的代表性(思路和方法),也是对自己这些年来做SQL优化的总结,对后面优化SQL有很好的提示和借鉴,案例会持续更新中。

说明:

最近优化慢SQL,执行计划错误和OR条件查询优化实战经验,提供优化SQL思路和方法:

1,利用exists来优化SQL(利用exists减少回表查询次数和确定驱动表)

2,OR语句优化(OR条件,字段有索引,无法使用索引的)

案例1:

  1. SELECT sum( CASE WHEN ols.check_status NOT IN ( 2, 3, 4 ) THEN 1 WHEN ols.check_status IS NULL THEN 1 END ) AS
    lesson_num, sum( ols.check_status = 1 ) AS attend_num FROM ol_live_student_time_chapter_list olstcl LEFT JOIN
    ol_live_student ols ON olstcl.live_student_id = ols.id inner JOIN ol_user u ON u.id = olstcl.user_id
  2. WHERE olstcl.class_course_type = '2' AND olstcl.attend_status <> 4
  3. AND olstcl.start_time >= 1621526400 AND olstcl.start_time < 1621612799 AND u.is_test_user = '0' AND
    counselor_id = '796' AND u.pay_status = '0' AND u.type = '1' AND u.STATUS = '1';

执行超过2.2秒,执行计划如下,从执行计划看:返回的rows也很少。key里用到了索引,按理说这个SQL是最优的SQL,没有优化的空间

但实际查看ol_user表,表有430万条数据,核心还是查询ol_user表的数据,如果能去掉ol_user表查询,就更好,如果不能去掉,能有其他优化方法

通过仔细分析3个表,各个条件查询查出的数据:

  1. select count(*) ol_live_student_time_chapter_list olstcl where olstcl.start_time>= 1621526400 AND
    olstcl.start_time< 1621612799 and olstcl.class_course_type = '2' AND olstcl.attend_status <> 4

发现ol_live_student_time_chapter_list 查询,这个时间查出只有8000多条。而查询oL_user表查询:

  1. select count(*) from ol_user u where u.is_test_user = '0' AND counselor_id = '796' AND u.pay_status = '0'
    AND u.type = '1' AND u.STATUS = '1'

查出300多条,但仔细计划显示:Using intersect(pay_status_user,counselor_id,type,is_test_user); Using where; Using index。 的确用很多索引,

这样看,表面看执行计划没问题,但实际看一下,这个ol_user表查询,其实只是一个条件,ol_user没有字段在select查出显示,从某种意义上讲,只需要查出符合条件的就可以,如果满足其中

一个就返回效率是否更高,不需要每个条件都查完,这样我们完全可以用EXISTS 替换inner join 来提高查询效率,SQL修改如下:

  1. SELECT sum( CASE WHEN ols.check_status NOT IN ( 2, 3, 4 ) THEN 1 WHEN ols.check_status IS NULL THEN 1 END ) AS
    lesson_num, sum( ols.check_status = 1 ) AS attend_num FROM ol_live_student_time_chapter_list olstcl LEFT JOIN
    ol_live_student ols ON olstcl.live_student_id = ols.id WHERE olstcl.class_course_type = '2' AND
    olstcl.attend_status <> 4 AND olstcl.start_time >= 1621526400 AND olstcl.start_time < 1621612799 and
    EXISTS (select 1 from ol_user u where u.is_test_user = '0' AND u.counselor_id
    = '796' AND u.pay_status = '0' AND u.type = '1' AND u.STATUS = '1' and u.id = olstcl.user_id) ;

使用优化的SQL,时间只有0.9秒左右

  优化原理:  利用 EXISTS 来替换 inner join,减少查询循环回表次数,提高效率。

案例2:

  1. select count(distinct u.id) from ol_user u left join ol_user_related_info uri on u.id = uri.user_id where
    u.type=1 and u.status =1 and u.pay_status=0 and uri.recovery_time_no_pay>=1622649600 and
    uri.recovery_time_no_pay<1622736000 and u.counselor_id in (403);

执行超过1.3秒,执行计划如下:

从这里看:查出ol_user的u.id,distinct汇总,而ol_user_related_info 是left join,仔细看逻辑,虽然是left join,但有uri.recovery_time_no_pay条件,就这个left join 可以改成inner join

可以改成: 

  1. select count(distinct uri.user_id) from ol_user u inner join ol_user_related_info uri on u.id = uri.user_id
    where u.type=1 and u.status =1and u.pay_status=0 and uri.recovery_time_no_pay>=1622649600 and
    uri.recovery_time_no_pay<1622736000 and u.counselor_id in (403);

修改完,2者的执行效率,查不多,改了也没优化,如上面的优化,我们知道ol_user表的数据量太大,要减少回表的查询,

该SQL就可以改成exists查询,如下:

  1. select count(distinct uri.user_id) from ol_user_related_info uri where uri.recovery_time_no_pay>=1622649600
    and uri.recovery_time_no_pay<1622736000 and exists (select 1 from ol_user u where u.id = uri.user_id and
    u.type=1 and u.status =1 and u.pay_status=0 and u.counselor_id in (403) )

改成这样,SQL执行只需0.6秒左右,未加索引,改写SQL,left join修改成 inner join,再改成exists查询

案例3

  1. SELECT f.flow_type,count(1) as num FROM `ol_admin_flow` `f` LEFT JOIN `ol_admin_flow_node` `n` ON `f`.`flowid`=
    `n`.`flowid` WHERE( `n`.`adminid` = 7417 OR `f`.`post_adminid` = 7417 ) GROUP BY `f`.`flow_type`;

执行超过2.5秒,post_adminid加索引,SQL也用不到索引,将or改写成 union all写法

  1. select flow_type,sum(num) num from (SELECT f.flow_type,1 num FROM ol_admin_flow f LEFT JOIN ol_admin_flow_node n
    ON f.flowid=n.flowid WHERE f.post_adminid = 7418 union all SELECT f.flow_type,1 num FROM ol_admin_flow f LEFT
    JOIN ol_admin_flow_node n ON f.flowid=n.flowid WHERE n.adminid = 7418 ) a group by flow_type

改成这样写法后。SQL效率大幅提升,查询只需0.3秒

案例4

  1. SELECT au.GROUP group_id,sum(IF (o.STATUS = 3 AND o.attach_pay_time BETWEEN 1625068800 AND 1627747199 AND
    o.rebate_time BETWEEN 1625068800 AND 1627747199, 0, oap.price ) ) real_renewal_money, sum(IF ( o.STATUS = 3
    AND o.attach_pay_time BETWEEN 1625068800 AND 1627747199 AND o.rebate_time BETWEEN 1625068800 AND
    1627747199, 0, oap.order_count ) ) renewal_num FROM `ol_order_attach_pay` `oap` INNER JOIN
    `ol_order` `o` IGNORE INDEX ( idx_pay_time_type ) ON `o`.`order_number` = `oap`.`order_number` AND `o`.`status`
    IN ( 1, 3 ) AND ( `o`.`attach_pay_time` >= 1625068800 AND `o`.`attach_pay_time` < 1627055999 ) AND
    `o`.`package_course_type` = 1 INNER JOIN `online`.`ol_admin_user` `au` ON `oap`.`adminid` = `au`.`id` WHERE
  2. `oap`.`status` = 1 AND `oap`.`adminid` IN ( 728, 818, 870, 1497, 2019, 2021,
  3. 2465, 2557, 2679, 3228, 3231, 3916, 3419, 3423, 3412, 3417, 3500, 4165,
    477, 4163, 1030, 562 )
  4. GROUP BY `au`.`group` LIMIT 100;

该SQL线上执行需要18秒以上,执行计划如下:

从执行计划看,慢的地方在查询ol_order的索引,有76万条。表中有以下索引:

KEY `attach_pay_time` (`attach_pay_time`)    KEY `package_course_type` (`package_course_type`),表中有20多个索引,太多,真不想再建索引。但不加索引无法优化,用 force index 索引优化效果又不太好,一开始研发建立复合索引:KEY `idx_pay_time_type` (`attach_pay_time`,`package_course_type`), 按理应该优化,但实际优化效果不好,后续将复合索引改成:

    KEY `idx_pay_time_type` (`package_course_type`,`attach_pay_time`),

加好后,执行计划如下,查询在0.5秒:   

案例5

 利用id主键来排序优化

  1. select q.q_id, q.q_title from bbs_question q where q.app_id = 'xtep' and q.delete_flag = 0 and
    ( q.ban_flag is null or q.ban_flag = 0 )
    and ( q.q_title like '%圆领%' or q.q_context like '%圆领%' ) order by q.create_time desc limit 10

优化说明:优化前因有OR和like查询,不好再建索引优化,查询需要2秒以上,因创建时间和主键自增长二者排序数据一致,order by q.create_time desc 改成 order by q.q_id desc ,SQL中create_time排序改成利用主键排序,优化后只需0.4秒。

案例6

 利用全文索引优化

  1. update sys_org_shop set path = REPLACE(path, '1806874642_1,6006991214_3,4006896323_4,4006910831_4,6006945801_6,
    5007352247_5,', '1806874642_1,6006991214_3,4006896323_4,4006910831_4,6006945737_6,5007352247_5,'),operation='
    1658136067459_method:SysOrgEnterprise.editOrgType' where path like '%1806874642_1,6006991214_3,4006896323_4,
    4006910831_4,6006945801_6,5007352247_5,%' and app_id='dep'

该语句执行平均1.9秒,查询更新path字段,在path建索引,效果不大,而且必须使用前后的%,效率很低,只能在path建立全文索引,运MySQL的版本是

5.6的,MySQL5.6全文索引不支持中文,但这个字段只支持数字和“,”。对优化该SQL足够,like模糊查询字段建立全文索引优化

  1. alter table sys_org_shop add FULLTEXT idx_path(path)

优化后SQL如下;注意AGAINST的挂号里面的有一对"",否则全文查询的数据不对

  1. update sys_org_shop set path = REPLACE(path, '1806874642_1,6006991214_3,4006896323_4,4006910831_4,6006945801_6,
    5007352247_5,', '1806874642_1,6006991214_3,4006896323_4,
    4006910831_4,6006945737_6,5007352247_5,'),
    operation='1658136067459_method:SysOrgEnterprise.editOrgType' where
    MATCH(`path`) AGAINST ('"1806874642_1,6006991214_3,4006896323_4,4006910831_4,6006945801_6,5007352247_5,"')
    and app_id='dep'

案例7

 利用普通索引优化like模糊查询

  1. select t.id,t.title,t.text_info,t.create_user,t.create_time,t.app_id,t.flag,u.name from sys_img_text t,sys_user
    u where t.create_user=u.sys_user_id and t.app_id = '3S' and t.title like '%27C01010%' and t.flag = 1 order
    by t.create_time desc limit 5

该SQL执行计划:

查询先走的create_time排序索引,再查询数据,执行起来要38秒+,很慢,可以在title字段建全文索引查询,但MySQL版本是5.6,

不支持中文,建了如查询中文的就查不出来,这里有app_id 有索引,可以用这个索引做一下过滤,过滤后在title字段模糊查询会快很多。

修改SQL 如下:

  1. select t.id,t.title,t.text_info,t.create_user,t.create_time,t.app_id,t.flag,u.name from sys_img_text t
    force index(idx_app),sys_user u where t.create_user=u.sys_user_id and t.app_id = '3S' and t.title
    like '%27C01010%' and t.flag = 1 orderby t.create_time desc limit 5

查询先过滤一层数据,在无全文索引情况下,增加force index(idx_app)强制索引查询,提高高查询效率,执行只需要0.011秒,

有几千倍的性能提升。

案例8

SQL在MySQL5.5执行正常,到了MySQL5.6和5.7执行一直很慢,执行查不出结果:(2023年3月31日补充案例)

  1. SELECT
  2. count( DISTINCT t1.seafarer_id ) AS hisNum,
  3. count( DISTINCT t2.seafarer_id ) AS jyNum
  4. FROM
  5. cy_cy_qf t1
  6. LEFT JOIN (
  7. SELECT
  8. t3.comp_id,
  9. t3.seafarer_id,
  10. t4.const_key,
  11. t4.const_value
  12. FROM
  13. gt_cp t4
  14. LEFT JOIN cy_cy_mf t3 ON t3.comp_id = t4.comp_id
  15. AND t3.memo_kind_id = t4.const_key
  16. WHERE
  17. t3.comp_id = 'COMP2967'
  18. AND t3.delete_flag = '0'
  19. ) t2 ON t1.comp_id = t2.comp_id
  20. AND t1.seafarer_id = t2.seafarer_id
  21. AND t2.const_value = '就医/看诊'
  22. WHERE
  23. t1.delete_flag = '0'
  24. AND t1.comp_id = 'COMP2967'

SQL的执行计划

从这看出gt_cp t4,这个表扫描的行数很多,通过t1.seafarer_id = t2.seafarer_id 查询,comp_id的值都相等,无过滤性。优化能从gt_cp优化,同时如果distinct t2.seafarer_id查出的数量很多,如果有很多重复的数据,在关联嵌套循环时,要多次循环,可以考虑尽量把t2的子循环数据减少,同时减少seafarer_id的数,修改后SQL如下:

  1. select count(distinct t1.seafarer_id) as hisNum,
  2. count(distinct t2.seafarer_id) as jyNum
  3. from
  4. cy_cy_qf t1
  5. left join
  6. (select (distinct t3.seafarer_id) seafarer_id
  7. from gt_cp t4
  8. left join cy_cy_mf t3
  9. on t3.comp_id = t4.comp_id and
  10. t3.memo_kind_id = t4.const_key
  11. where
  12. t3.comp_id = 'COMP2967' and
  13. t3.delete_flag = '0' and t4.const_value = '就医/看诊') t2
  14. on t1.seafarer_id = t2.seafarer_id
  15. where
  16. t1.delete_flag = '0' and
  17. t1.comp_id = 'COMP2967'

SQL修改如下后,执行从无法查出数据降到只需0.2秒,同时没有建任何索引,性能大幅提升。

案例9

SQL执行一直很慢,需要6秒才能有结果,SQL如下:(2023年4月25日补充案例)

  1. SELECT
  2. m.id,
  3. m.comp_id,
  4. m.vessel_id,
  5. m.dept_id,
  6. m.vessel_name,
  7. m.device_id,
  8. d.device_name,
  9. m.order_type,
  10. m.plan_date,
  11. t.execution_content,
  12. t.plan_content,
  13. t.sp_out_store_id spOutStoreId,
  14. t.ma_out_store_id maOutStoreId,
  15. d.parent_device_id,
  16. d.parent_device_name,
  17. d.device_level_flag
  18. FROM
  19. mw_view m
  20. LEFT JOIN mw_order t ON m.id = t.id
  21. AND m.comp_id = t.comp_id
  22. LEFT JOIN md_info d ON m.device_id = d.id
  23. WHERE
  24. 1 = 1
  25. AND m.comp_id = 'e98aab51fcd940f3b7aee670a2ea4207'
  26. AND (
  27. m.vessel_id = '570ca38b05344c5daa30d00fccae8512'
  28. OR m.vessel_id = 'd66417caacdd46a4918619eee0239d50'
  29. OR m.vessel_id = '5a67345cb19c4e449243423285c60550'
  30. OR m.vessel_id = 'e34a6304d1cc4fbd962cf852a04bcba3'
  31. OR m.vessel_id = '35c9f507cd9645338c45588f00f24b6f'
  32. OR m.vessel_id = 'f668e782b2b14e58b5346998d9a3be75'
  33. OR m.vessel_id = '455393531f28411c86eaab071685664b'
  34. OR m.vessel_id = 'cf5b84600d38494aa97c1824666d2eca'
  35. OR m.vessel_id = '5156d56d648f471eb5ac4173db63842c'
  36. OR m.vessel_id = 'ee5d5d871f6045088c5b5817193d5962'
  37. OR m.vessel_id = '2fe2b46255ca4cad855f2476af496d76'
  38. OR m.vessel_id = '0aa93557f6794c13ab14a4a432c5b7ed'
  39. OR m.vessel_id = 'a2d587d2247d4426bec020776226f827'
  40. OR m.vessel_id = '18437c78e58e4768bcec849f0b7bc8c4'
  41. OR m.vessel_id = '5415db21fa7e47629d6563885aea92b5'
  42. OR m.vessel_id = '6c534cd5ebfd42159c3a852c078ccc99'
  43. OR m.vessel_id = 'e2d6568bfd614f5a92d2c25c13d56c02'
  44. OR m.vessel_id = 'e87f3c7b33de4779a8bd5ad4c9fab00c'
  45. OR m.vessel_id = '7114381f6bf2467dac1cb26d4a56ca71'
  46. OR m.vessel_id = '48af2c42c3284c76b88cf2f57d34dc35'
  47. OR m.vessel_id = 'b2fd6818be5346399a8db6785db1b20e'
  48. OR m.vessel_id = '1d11b69b76224b5cbef8dfc0f5d37dda'
  49. OR m.vessel_id = 'dd96e7b0658043889b67378552f91950'
  50. OR m.vessel_id = '6ede3b68b0aa4aed9f54c90162701a80'
  51. OR m.vessel_id = 'bda55c44da6f4b28a79e0eb2451afe7b'
  52. OR m.vessel_id = '561d8e443e234507b72177fd65e947d5'
  53. OR m.vessel_id = '5d52046476164e84af8998496c2d1227'
  54. )
  55. AND ( m.order_type = 'delay' OR m.order_type = 'warn' )
  56. AND (
  57. m.status_flag = '00'
  58. OR m.status_flag = '55'
  59. OR m.status_flag = '45'
  60. OR m.status_flag = '35'
  61. OR m.status_flag = '07'
  62. OR m.status_flag = '50'
  63. OR m.status_flag = '40'
  64. OR m.status_flag = '30'
  65. )
  66. ORDER BY
  67. m.order_type,
  68. m.vessel_name,
  69. m.plan_date
  70. LIMIT 0,
  71. 10;

查看执行计划,表中加了 idx_cvs(comp_id,vessel_id,status_flag)索引也没有用,特别vessel_id 是in太多数据,和业务开发聊,能否减少vessel_id 值,回答是不能减少,测试发现去掉一半的vessel_id 值,就很快,查询就只需要查询0.2秒内,那如果不减少值,怎么才能提高效率,有其他办法?

后来考虑是否把这个vessel_id 的值,分成2个SQL再Union all 起来,这样满足程序逻辑,又提高效率,测试的确可以,修改的SQL如下,SQL从以前的6秒降到0.2秒左右:

  1. select * from (
  2. SELECT
  3. m.id,
  4. m.comp_id,
  5. m.vessel_id,
  6. m.dept_id,
  7. m.vessel_name,
  8. m.device_id,
  9. d.device_name,
  10. m.order_type,
  11. m.plan_date,
  12. t.execution_content,
  13. t.plan_content,
  14. t.sp_out_store_id spOutStoreId,
  15. t.ma_out_store_id maOutStoreId,
  16. d.parent_device_id,
  17. d.parent_device_name,
  18. d.device_level_flag
  19. FROM
  20. mw_view m
  21. LEFT JOIN mw_order t ON m.id = t.id
  22. AND m.comp_id = t.comp_id
  23. LEFT JOIN md_info d ON m.device_id = d.id
  24. WHERE
  25. 1 = 1
  26. AND m.comp_id = 'e98aab51fcd940f3b7aee670a2ea4207'
  27. AND (
  28. m.vessel_id in('570ca38b05344c5daa30d00fccae8512' ,
  29. 'd66417caacdd46a4918619eee0239d50' ,
  30. '5a67345cb19c4e449243423285c60550' ,
  31. 'e34a6304d1cc4fbd962cf852a04bcba3' ,
  32. '35c9f507cd9645338c45588f00f24b6f' ,
  33. 'f668e782b2b14e58b5346998d9a3be75' ,
  34. '455393531f28411c86eaab071685664b' ,
  35. 'cf5b84600d38494aa97c1824666d2eca' ,
  36. '5156d56d648f471eb5ac4173db63842c' ,
  37. 'ee5d5d871f6045088c5b5817193d5962' ,
  38. '2fe2b46255ca4cad855f2476af496d76' ,
  39. '0aa93557f6794c13ab14a4a432c5b7ed' ,
  40. 'a2d587d2247d4426bec020776226f827' ,
  41. '18437c78e58e4768bcec849f0b7bc8c4'
  42. )
  43. )
  44. AND ( m.order_type = 'delay' OR m.order_type = 'warn' )
  45. AND (
  46. m.status_flag = '00'
  47. OR m.status_flag = '55'
  48. OR m.status_flag = '45'
  49. OR m.status_flag = '35'
  50. OR m.status_flag = '07'
  51. OR m.status_flag = '50'
  52. OR m.status_flag = '40'
  53. OR m.status_flag = '30'
  54. )
  55. union all
  56. SELECT
  57. m.id,
  58. m.comp_id,
  59. m.vessel_id,
  60. m.dept_id,
  61. m.vessel_name,
  62. m.device_id,
  63. d.device_name,
  64. m.order_type,
  65. m.plan_date,
  66. t.execution_content,
  67. t.plan_content,
  68. t.sp_out_store_id spOutStoreId,
  69. t.ma_out_store_id maOutStoreId,
  70. d.parent_device_id,
  71. d.parent_device_name,
  72. d.device_level_flag
  73. FROM
  74. mw_view m
  75. LEFT JOIN mw_order t ON m.id = t.id
  76. AND m.comp_id = t.comp_id
  77. LEFT JOIN md_info d ON m.device_id = d.id
  78. WHERE
  79. 1 = 1
  80. AND m.comp_id = 'e98aab51fcd940f3b7aee670a2ea4207'
  81. AND (
  82. m.vessel_id in(
  83. '5415db21fa7e47629d6563885aea92b5' ,
  84. '6c534cd5ebfd42159c3a852c078ccc99' ,
  85. 'e2d6568bfd614f5a92d2c25c13d56c02' ,
  86. 'e87f3c7b33de4779a8bd5ad4c9fab00c',
  87. '7114381f6bf2467dac1cb26d4a56ca71' ,
  88. '48af2c42c3284c76b88cf2f57d34dc35' ,
  89. 'b2fd6818be5346399a8db6785db1b20e' ,
  90. '1d11b69b76224b5cbef8dfc0f5d37dda',
  91. 'dd96e7b0658043889b67378552f91950' ,
  92. '6ede3b68b0aa4aed9f54c90162701a80' ,
  93. 'bda55c44da6f4b28a79e0eb2451afe7b' ,
  94. '561d8e443e234507b72177fd65e947d5' ,
  95. '5d52046476164e84af8998496c2d1227' )
  96. )
  97. AND ( m.order_type = 'delay' OR m.order_type = 'warn' )
  98. AND (
  99. m.status_flag = '00'
  100. OR m.status_flag = '55'
  101. OR m.status_flag = '45'
  102. OR m.status_flag = '35'
  103. OR m.status_flag = '07'
  104. OR m.status_flag = '50'
  105. OR m.status_flag = '40'
  106. OR m.status_flag = '30'
  107. )
  108. ) t
  109. ORDER BY
  110. t.order_type,
  111. t.vessel_name,
  112. t.plan_date
  113. LIMIT 0,
  114. 10

案例10

 该SQL执行2秒左右,优化方法: 改变驱动表SQL如下,从depot_transfer_goods 表改成查询depot_transfer 表,改变和减少驱动表行数,来优化SQL。(案例日期:2023年12月28日)

  1. SELECT a.*, b.type, b.status, b.allotTime, b.from
  2. FROM depot_transfer_goods a
  3. INNER JOIN depot_transfer b ON a.evidenceNo = b.no
  4. WHERE b.depotId = 1
  5. AND b.deleteStatus = 0
  6. AND a.deleteStatus = 0
  7. AND b.type IN (1)
  8. AND b.status IN (3)
  9. AND b.createTime BETWEEN 1672502400 AND 1702223999
  10. ORDER BY b.createTime DESC
  11. LIMIT 0, 12

优化后0.2秒左右,

  1. select a.*, b.type, b.status, b.allotTime, b.from from (
  2. select * from depot_transfer b where createTime BETWEEN 1672502400 AND 1702223999
  3. and b.depotId = 1
  4. AND b.deleteStatus = 0
  5. AND b.type IN (1)
  6. AND b.status IN (3)
  7. ) b inner join depot_transfer_goods a on a.evidenceNo = b.no
  8. ORDER BY b.createTime DESC
  9. LIMIT 0, 12

案例11

改变SQL的关联字段来优化,该SQL执行5秒左右,优化方法:改变关联字段,因mysql优化引擎不能传递相等字段(没有谓词下推),将ON t8.materialCode = t.materialCode 改成 ON t8.materialCode = t6.materielCode ,

t.materialCode,这t表是查询表,需t查询出来才能关联t8,改成实体表t6,性能更好 。 (案例日期:2023年12月28日)

  1. SELECT t6.materielCode, t6.cargoOwnerCode, t6.depotCode
  2. , CONCAT(t6.materielCode, '-', t6.cargoOwnerCode, '-', t6.depotCode) AS threeCode
  3. , CAST(SYSDATE() AS DATE) AS outDate, t6.stockAvailable
  4. , IF(t8.id, 1, 0) AS isHighProfit
  5. FROM (
  6. SELECT t1.materialCode, t1.shopId, t3.supplier_code
  7. FROM oms_order_parent t
  8. INNER JOIN oms_order_goods t1 ON t1.orderParentNo = t.orderNo
  9. INNER JOIN cloud_shop t3 ON t3.id = t1.shopId
  10. WHERE t.createTime >= 1699545600
  11. AND t.warehouseCode = 'CK0001'
  12. AND (t.returnStatus IN (1, 3, 5)
  13. OR (t.returnStatus = 4
  14. AND t.warehouseOutStatus = 2))
  15. GROUP BY t1.materialCode, t.warehouseCode, t3.supplier_code
  16. ) t
  17. INNER JOIN wms_depot_stock_collect t6
  18. ON t6.materielCode = t.materialCode
  19. AND t6.cargoOwnerCode = t.supplier_code
  20. AND t6.depotCode = 'CK0001'
  21. AND t6.stockAvailable <= 20
  22. AND t6.commodityType != 2
  23. LEFT JOIN customer_material_factor_recommend t8
  24. ON t8.materialCode = t.materialCode
  25. AND t8.factorType = 'HIGHPROFIT'

修改SQL,优化后0.42秒

  1. SELECT t6.materielCode, t6.cargoOwnerCode, t6.depotCode
  2. , CONCAT(t6.materielCode, '-', t6.cargoOwnerCode, '-', t6.depotCode) AS threeCode
  3. , CAST(SYSDATE() AS DATE) AS outDate, t6.stockAvailable
  4. , IF(t8.id, 1, 0) AS isHighProfit
  5. FROM (
  6. SELECT t1.materialCode, t1.shopId, t3.supplier_code
  7. FROM oms_order_parent t
  8. INNER JOIN oms_order_goods t1 ON t1.orderParentNo = t.orderNo
  9. INNER JOIN cloud_shop t3 ON t3.id = t1.shopId
  10. WHERE t.createTime >= 1699545600
  11. AND t.warehouseCode = 'CK0001'
  12. AND (t.returnStatus IN (1, 3, 5)
  13. OR (t.returnStatus = 4
  14. AND t.warehouseOutStatus = 2))
  15. GROUP BY t1.materialCode, t.warehouseCode, t3.supplier_code
  16. ) t
  17. INNER JOIN wms_depot_stock_collect t6
  18. ON t6.materielCode = t.materialCode
  19. AND t6.cargoOwnerCode = t.supplier_code
  20. AND t6.depotCode = 'CK0001'
  21. AND t6.stockAvailable <= 20
  22. AND t6.commodityType != 2
  23. LEFT JOIN customer_material_factor_recommend t8
  24. ON t8.materialCode = t6.materielCode
  25. AND t8.factorType = 'HIGHPROFIT'

案例12

该SQL执行8秒左右,还使用了like的%查询,优化方法:将like语句的查询作为in条件,不要使用关联,避免优化引擎走错误的执行计划。 (案例日期:2023年12月28日)

  1. SELECT count(DISTINCT t2.channelId) AS channelIdNum, count(DISTINCT t2.districtId) AS districtIdNum
  2. FROM (
  3. SELECT t.customerCode, t.materialCode
  4. FROM crm_behavior_recommend t
  5. INNER JOIN erp_base_customer t6
  6. ON t.customerCode = t6.customerCode
  7. AND t6.isStaff = 0
  8. WHERE t.createTime >= FROM_UNIXTIME(1701964800)
  9. AND t.createTime <= FROM_UNIXTIME(1702051199)
  10. AND t.behaviorType IN (2, 3)
  11. UNION
  12. SELECT t.buyerCode, t2.materialCode
  13. FROM oms_order_parent t
  14. INNER JOIN oms_order_picking t1 ON t.orderNo = t1.orderNo
  15. INNER JOIN oms_order_goods t2 ON t2.orderParentNo = t.orderNo
  16. INNER JOIN erp_base_customer t6
  17. ON t.buyerCode = t6.customerCode
  18. AND t6.isStaff = 0
  19. WHERE t.createTime >= 1701964800
  20. AND t.createTime <= 1702051199
  21. AND t.returnStatus != 4
  22. AND t1.returnStatus != 4
  23. ) t
  24. INNER JOIN oms_material_basics t1 ON t.materialCode = t1.materialCode
  25. INNER JOIN erp_base_customer t2
  26. ON t.customerCode = t2.customerCode
  27. AND t2.isStaff = 0
  28. LEFT JOIN (
  29. SELECT t.id, t.name_path AS newCategoryName, t.category_name AS categoryName, t.id_path
  30. FROM `db_baseinfo`.xdnet_basic_category t
  31. WHERE t.category_level = 5
  32. ) t7
  33. ON t7.id = t1.newCategoryId
  34. WHERE CONCAT(',', t7.id_path, ',') LIKE CONCAT('%,', 2394, ',%')

优化后0.3秒左右

  1. select count(DISTINCT t1.channelId) AS channelIdNum, count(DISTINCT t1.districtId) AS districtIdNum from (
  2. SELECT DISTINCT t2.channelId , t2.districtId,t1.newCategoryId
  3. FROM (
  4. SELECT t.customerCode, t.materialCode
  5. FROM crm_behavior_recommend t
  6. INNER JOIN erp_base_customer t6
  7. ON t.customerCode = t6.customerCode
  8. AND t6.isStaff = 0
  9. WHERE t.createTime >= FROM_UNIXTIME(1702224000)
  10. AND t.createTime <= FROM_UNIXTIME(1702310399)
  11. AND t.behaviorType IN (2, 3)
  12. UNION
  13. SELECT t.buyerCode, t2.materialCode
  14. FROM oms_order_parent t
  15. INNER JOIN oms_order_picking t1 ON t.orderNo = t1.orderNo
  16. INNER JOIN oms_order_goods t2 ON t2.orderParentNo = t.orderNo
  17. INNER JOIN erp_base_customer t6
  18. ON t.buyerCode = t6.customerCode
  19. AND t6.isStaff = 0
  20. WHERE t.createTime >= 1702224000
  21. AND t.createTime <= 1702310399
  22. AND t.returnStatus != 4
  23. AND t1.returnStatus != 4
  24. ) t
  25. INNER JOIN oms_material_basics t1 ON t.materialCode = t1.materialCode
  26. INNER JOIN erp_base_customer t2
  27. ON t.customerCode = t2.customerCode
  28. AND t2.isStaff = 0
  29. ) t1
  30. where t1.newCategoryId in (
  31. SELECT t.id
  32. FROM xdnet_basic_category t
  33. WHERE t.category_level = 5 and CONCAT(',', t.id_path, ',') LIKE CONCAT('%,', 2394, ',%')
  34. )

总结:

1,优化SQL,MySQL需要选择正确的驱动表,如果执行计划不正确,可用exists来明确驱动表

2,加复合索引,最好先测试执行效果,有时预想和实际不一样

    3,转变优化思路:如like查询,无全文索引下,利用普通索引大量过滤当前表的数据行,再like查询,以提高查询效率。
 

MySQL的经典SQL优化12例(更新于2023年12月28日)的更多相关文章

  1. MySQL派生表(derived)优化一例

    1.什么是派生表derived 关键字:子查询–>在From后where前的子查询 mysql; +----+-------------+------------+------+-------- ...

  2. (6)MySQL进阶篇SQL优化(MyISAM表锁)

    1.MySQL锁概述 锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算资源 (如 CPU.RAM.I/O 等)的抢占以外,数据也是一种供许多用户共享的资源.如何保证数 据并 ...

  3. GIS大讲堂内所有讲座的索引(更新至2008年6月26日)(转)

    转自:http://www.cnblogs.com/xiexiaokui/archive/2008/11/20/1337934.html GIS大讲堂内所有讲座的索引(更新至2008年6月26日)   ...

  4. 全国Uber优步司机奖励政策 (12月28日-1月3日)

    本周已经公开奖励整的城市有:北 京.成 都.重 庆.上 海.深 圳.长 沙.佛 山.广 州.苏 州.杭 州.南 京.宁 波.青 岛.天 津.西 安.武 汉.厦 门,可按CTRL+F,搜城市名快速查找. ...

  5. 北京Uber优步司机奖励政策(12月28日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  6. 武汉Uber优步司机奖励政策(12月28日到1月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. 天津Uber优步司机奖励政策(12月28日到12月29日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  8. 南京Uber优步司机奖励政策(12月28日到1月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. 佛山Uber优步司机奖励政策(12月28日到1月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  10. 青岛Uber优步司机奖励政策(12月28日到1月3日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

随机推荐

  1. ONVIF网络摄像头(IPC)客户端开发—RTSP RTCP RTP加载AAC音频流

    前言: RTSP,RTCP,RTP一般是一起使用,在FFmpeg和live555这些库中,它们为了更好的适用性,所以实现起来非常复杂,直接查看FFmpeg和Live555源代码来熟悉这些协议非常吃力, ...

  2. Python学习之十五_不同类型数据库表内容比较

    Python学习只十五_不同类型数据库表内容比较 前言 最近学习力总结了很多Python相关的内容 本次想继续学习一下不同数据库之间的数据比较. 这样理论上可以极大的缩减不同数据库测试成本. 感谢Py ...

  3. [转帖]Spring为啥不推荐使用@Autowired注解?

    https://my.oschina.net/u/5079097/blog/5289666   引言 使用IDEA开发时,同组小伙伴都喜欢用@Autowired注入,代码一片warning,看着很不舒 ...

  4. [转帖]TIDB TIKV 数据是怎么写入与通过Region 分割的?

    https://cloud.tencent.com/developer/article/1882194 国产的分布式数据库不少,TDSQL, OB, TIDB ,等等都是比较知名的产品,使用的分布式协 ...

  5. [转帖]jmeter之foreach循环控制器-03篇

    上篇我们通过正则表达式获取到了一组数据,那么怎么来用呢?下面就用foreach控制器来使用结果,如下图所示 然后再foreach控制器里添加要循环的请求,我们模拟百度搜索,value填入${id} 然 ...

  6. [转帖]解释docker单机部署kraft模式kafka集群时,尝试各种方式的网络broker全部不通而启动失败的原因,并提示常见bug关注点

    现象: controller节点与其他两个broker的通信失败.公网ip,宿主机ip,服务名,各种网络方式,都无法成功. 两点提示: 1.bug原因:因为单机内存不够用,设置了较低的 KAFKA_H ...

  7. [转帖] Linux命令拾遗-查看系统信息

    https://www.cnblogs.com/codelogs/p/16060714.html 简介# 作为一名程序员,有时需要关注自己的进程运行在什么样的软硬件环境里,比如几核cpu.固态硬盘还是 ...

  8. ipset的学习与使用

    ipset的学习与使用 场景说明 虽然可以通过: firewall-cmd --zone=trusted --add-source=$1 --permanent && firewall ...

  9. [转帖]【JVM】GC算法与垃圾收集器

    引入 java 语言中一个显著的特点就是引入了java回收机制,是c++程序员最头疼的内存管理的问题迎刃而解,它使得java程序员在编写程序的时候不在考虑内存管理.由于有个垃圾回收机制,可以有效的防止 ...

  10. [转帖]UseG1GC垃圾回收技术解析

    https://www.cnblogs.com/yuanzipeng/p/13374690.html 介绍 G1 GC,全称Garbage-First Garbage Collector,通过-XX: ...