我有一个SQL 查询起来特别慢,怎么办优化

我纯洁全身都纯洁 2018-04-08 08:25:20
这个sql是别人写的,特别慢,人离职了,这个锅只好我背了。。。

SELECT COUNT(*) FROM SN_VIPLOADBALANCE A
LEFT JOIN SN_VIPLOADINFO_SERVER B ON B.VIPSVR_ROWID=A.ROW_ID AND B.VIPSVR_STATUS != 'DEL'
LEFT JOIN SN_VIPLOADINFO_HGP H ON A.ROW_ID = H.VIPHGP_ROWID AND B.VIPSVR_NUM = H.VIPHGP_NUM AND H.VIPHGP_STATUS != 'DEL'
LEFT JOIN SN_HOSTGROUP J ON H.VIPHGP_HGPNUM = J.HOSTGROUP_NUM AND J.HOSTGROUP_STATUS != 'DEL'
LEFT JOIN SN_SERVER C ON C.SERVER_NUM = B.VIPSVR_SVRNUM AND C.SERVER_STATUS != 'DEL'
LEFT JOIN SN_SERVER_HISTORY I ON I.SVRNUM = B.VIPSVR_SVRNUM AND I.OPERATETYPE = 'DEL'
LEFT JOIN SN_SYSTEM K ON I.SYSCNAME = K.SYSTEM_FCNAME AND K.SYSTEM_STATUS != 'DEL'
LEFT JOIN SN_RACKDEV RD ON C.SERVER_SVRNUM = RD.RACKDEV_DEVNUM
LEFT JOIN SN_RACK R ON RD.RACKDEV_RACKNUM = R.RACK_NUM AND R.RACK_STATUS != 'DEL'
LEFT JOIN SN_LOGICROOM L ON R.RACK_LOGICROOMNUM = L.LOGICROOM_NUM AND L.LOGICROOM_STATUS != 'DEL'
LEFT JOIN SN_INTRANETIP E ON E.INTRANETIP_DEVNUM = A.VIP_NLBNUM AND E.INTRANETIP_STATUS != 'DEL'
LEFT JOIN SN_INTRANETIP NW ON NW.INTRANETIP_DEVNUM = A.VIP_VIPNUM AND NW.INTRANETIP_STATUS != 'DEL'
LEFT JOIN SN_INTERNETIP GW ON GW.INTERNETIP_BUSINESSNUM = A.VIP_VIPNUM AND GW.INTERNETIP_STATUS != 'DEL'
WHERE 1=1
AND A.VIP_NLBNUM != ''
AND A.VIP_NLBNUM IS NOT NULL
AND A.VIP_STATUS != 'DEL'
AND B.VIPSVR_SVRNUM IS NOT NULL

其中:
A表:11197
B表:272650
H表:12058
J表:834
C表:35783
I表:38739
K表:38739
RD表:3662
R表:788
L表:16
E、NW表:418220
GW表:2830

执行计划:

执行时间7秒以上。
表之间好多1对多关系。
...全文
1601 33 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
小灰狼 2018-04-20
  • 打赏
  • 举报
回复
引用 29 楼 hjgzj 的回复:
1、状态都是!=, 因为状态存在DEL、正常等等几种,现在要查非删除的,整个项目都是这样写的。没有人用IN 2、1=1的问题,这个真不是问题 去掉速度不变,包括count(*)的问题,我知道一般人都是认为1比*快,其实那都是从书上看来得,我大数据测试过*比主键快比1快。 3、=两边数据类型都是varchar类型,这点直接可以从上图的执行计划上看出来,如果一边是int一边是varchar,执行计划上就会有不止一个ALL 4、上面回答的问题基本上都是基于“索引”、count(*)、“1=1”来回答,我就想问你们都不知道怎么看执行计划吗?
执行计划相信都看了,只是发现索引什么的都用上了,所以没有什么可改进的空间 我的想法是,如果能够把一些查询条件放到子查询中去,让子查询得到的中间结果尽量少一点,这样在进行联接运算时,数据量少一点会不会更快一点
  • 打赏
  • 举报
回复
引用 32 楼 hemowolf 的回复:
[quote=引用 29 楼 hjgzj 的回复:] 1、状态都是!=, 因为状态存在DEL、正常等等几种,现在要查非删除的,整个项目都是这样写的。没有人用IN 2、1=1的问题,这个真不是问题 去掉速度不变,包括count(*)的问题,我知道一般人都是认为1比*快,其实那都是从书上看来得,我大数据测试过*比主键快比1快。 3、=两边数据类型都是varchar类型,这点直接可以从上图的执行计划上看出来,如果一边是int一边是varchar,执行计划上就会有不止一个ALL 4、上面回答的问题基本上都是基于“索引”、count(*)、“1=1”来回答,我就想问你们都不知道怎么看执行计划吗?
执行计划相信都看了,只是发现索引什么的都用上了,所以没有什么可改进的空间 我的想法是,如果能够把一些查询条件放到子查询中去,让子查询得到的中间结果尽量少一点,这样在进行联接运算时,数据量少一点会不会更快一点[/quote] 算了 我已经放弃改造这个了
小灰狼 2018-04-19
  • 打赏
  • 举报
回复
引用 24 楼 hjgzj 的回复:
把where之后的条件放前面,查询的结果就会少。
居然会少?不应该呀! 有没有对比一下,少的是哪些数据? 另外,放到前面去之后,查询速度有没有提升?
阿闰 2018-04-19
  • 打赏
  • 举报
回复
引用 29 楼 hjgzj 的回复:
[quote=引用 28 楼 wasd986523 的回复:] 楼上讲了那么多 24楼 和1=1去掉应该有点用, 我补充下这么多left join为何不用inner join, 如果没有lfet join 是不是会快很多,仔细分析下表结构有没有交集 包含关系,看你条件几乎都是 !=DEL, 为什么不用=而用!=呢? 如果以上还不行,检查每个 !=两边的数据类型 是varchar 还是number 或者其他,如果数据类型不一致,一定要转成相同的 还不行 就建索引,最后没办法 改表关系 或表结构 或者建存储过程 查询
1、状态都是!=, 因为状态存在DEL、正常等等几种,现在要查非删除的,整个项目都是这样写的。没有人用IN 2、1=1的问题,这个真不是问题 去掉速度不变,包括count(*)的问题,我知道一般人都是认为1比*快,其实那都是从书上看来得,我大数据测试过*比主键快比1快。 3、=两边数据类型都是varchar类型,这点直接可以从上图的执行计划上看出来,如果一边是int一边是varchar,执行计划上就会有不止一个ALL 4、上面回答的问题基本上都是基于“索引”、count(*)、“1=1”来回答,我就想问你们都不知道怎么看执行计划吗?[/quote]好牛逼的样子噢,
  • 打赏
  • 举报
回复
引用 25 楼 AHUA1001 的回复:
项目涉密吗,能把建表脚本和数据发给我一份吗。
这个真不能,公司的数据
  • 打赏
  • 举报
回复
引用 28 楼 wasd986523 的回复:
楼上讲了那么多 24楼 和1=1去掉应该有点用, 我补充下这么多left join为何不用inner join, 如果没有lfet join 是不是会快很多,仔细分析下表结构有没有交集 包含关系,看你条件几乎都是 !=DEL, 为什么不用=而用!=呢? 如果以上还不行,检查每个 !=两边的数据类型 是varchar 还是number 或者其他,如果数据类型不一致,一定要转成相同的 还不行 就建索引,最后没办法 改表关系 或表结构 或者建存储过程 查询
1、状态都是!=, 因为状态存在DEL、正常等等几种,现在要查非删除的,整个项目都是这样写的。没有人用IN 2、1=1的问题,这个真不是问题 去掉速度不变,包括count(*)的问题,我知道一般人都是认为1比*快,其实那都是从书上看来得,我大数据测试过*比主键快比1快。 3、=两边数据类型都是varchar类型,这点直接可以从上图的执行计划上看出来,如果一边是int一边是varchar,执行计划上就会有不止一个ALL 4、上面回答的问题基本上都是基于“索引”、count(*)、“1=1”来回答,我就想问你们都不知道怎么看执行计划吗?
阿闰 2018-04-18
  • 打赏
  • 举报
回复
楼上讲了那么多 24楼 和1=1去掉应该有点用, 我补充下这么多left join为何不用inner join, 如果没有lfet join 是不是会快很多,仔细分析下表结构有没有交集 包含关系,看你条件几乎都是 !=DEL, 为什么不用=而用!=呢? 如果以上还不行,检查每个 !=两边的数据类型 是varchar 还是number 或者其他,如果数据类型不一致,一定要转成相同的 还不行 就建索引,最后没办法 改表关系 或表结构 或者建存储过程 查询
大宝贱 2018-04-17
  • 打赏
  • 举报
回复
问清楚功能需求,自己重写吧。这种古董,作者也不一定知道了。
AHUA1001 2018-04-12
  • 打赏
  • 举报
回复
项目涉密吗,能把建表脚本和数据发给我一份吗。
小灰狼 2018-04-10
  • 打赏
  • 举报
回复
瞎说几点建议啊: 1、把 where 后面的条件放到前面去,变成各个表的子查询 2、把 != 'DEL' 的条件全部放到 On 的最前面,先进行判断

SELECT COUNT(*) FROM 
(Select ROW_ID, VIP_NLBNUM, VIP_VIPNUM From SN_VIPLOADBALANCE Where A.VIP_NLBNUM != '' AND A.VIP_NLBNUM IS NOT NULL AND A.VIP_STATUS != 'DEL') A
LEFT JOIN (Select VIPSVR_ROWID, VIPSVR_NUM, VIPSVR_SVRNUM From SN_VIPLOADINFO_SERVER Where VIPSVR_SVRNUM IS NOT NULL And VIPSVR_STATUS != 'DEL')B ON B.VIPSVR_ROWID=A.ROW_ID
LEFT JOIN SN_VIPLOADINFO_HGP H ON H.VIPHGP_STATUS != 'DEL' And A.ROW_ID = H.VIPHGP_ROWID AND B.VIPSVR_NUM = H.VIPHGP_NUM 
LEFT JOIN SN_HOSTGROUP J ON J.HOSTGROUP_STATUS != 'DEL' And H.VIPHGP_HGPNUM = J.HOSTGROUP_NUM
LEFT JOIN SN_SERVER C ON C.SERVER_STATUS != 'DEL' And C.SERVER_NUM = B.VIPSVR_SVRNUM 
LEFT JOIN SN_SERVER_HISTORY I I.OPERATETYPE = 'DEL' And ON I.SVRNUM = B.VIPSVR_SVRNUM
LEFT JOIN SN_SYSTEM K ON K.SYSTEM_STATUS != 'DEL' And I.SYSCNAME = K.SYSTEM_FCNAME
LEFT JOIN SN_RACKDEV RD ON C.SERVER_SVRNUM = RD.RACKDEV_DEVNUM
LEFT JOIN SN_RACK R ON R.RACK_STATUS != 'DEL' And RD.RACKDEV_RACKNUM = R.RACK_NUM
LEFT JOIN SN_LOGICROOM L ON L.LOGICROOM_STATUS != 'DEL' And R.RACK_LOGICROOMNUM = L.LOGICROOM_NUM 
LEFT JOIN SN_INTRANETIP E ON E.INTRANETIP_STATUS != 'DEL' And E.INTRANETIP_DEVNUM = A.VIP_NLBNUM 
LEFT JOIN SN_INTRANETIP NW ON NW.INTRANETIP_STATUS != 'DEL' And NW.INTRANETIP_DEVNUM = A.VIP_VIPNUM 
LEFT JOIN SN_INTERNETIP GW ON GW.INTERNETIP_STATUS != 'DEL' And GW.INTERNETIP_BUSINESSNUM = A.VIP_VIPNUM 
3、或者再试试把各个表单独的条件放到表中,作为一个子查询,on 后面只放与连接有关的条件

SELECT COUNT(*) 
FROM 
(Select ROW_ID, VIP_NLBNUM, VIP_VIPNUM From SN_VIPLOADBALANCE Where VIP_NLBNUM != '' AND VIP_NLBNUM IS NOT NULL AND VIP_STATUS != 'DEL') A
LEFT JOIN (
	Select VIPSVR_ROWID, VIPSVR_NUM, VIPSVR_SVRNUM From SN_VIPLOADINFO_SERVER Where B.VIPSVR_STATUS != 'DEL' And VIPSVR_SVRNUM IS NOT NULL
)B ON B.VIPSVR_ROWID = A.ROW_ID
LEFT JOIN (
	Select VIPHGP_ROWID, VIPHGP_NUM, VIPHGP_HGPNUM From SN_VIPLOADINFO_HGP Where VIPHGP_STATUS != 'DEL'
)H ON A.ROW_ID = H.VIPHGP_ROWID AND B.VIPSVR_NUM = H.VIPHGP_NUM
LEFT JOIN (
	Select HOSTGROUP_NUM From SN_HOSTGROUP Where HOSTGROUP_STATUS != 'DEL'
)J ON H.VIPHGP_HGPNUM = J.HOSTGROUP_NUM 
LEFT JOIN (
	Select SERVER_NUM From SN_SERVER Where SERVER_STATUS != 'DEL'
) C ON C.SERVER_NUM = B.VIPSVR_SVRNUM 
LEFT JOIN (
	Select SVRNUM, SYSCNAME From SN_SERVER_HISTORY Where OPERATETYPE = 'DEL'
)I ON I.SVRNUM = B.VIPSVR_SVRNUM 
LEFT JOIN (
	Select SYSTEM_FCNAME From SN_SYSTEM Where SYSTEM_STATUS != 'DEL'
)K ON I.SYSCNAME = K.SYSTEM_FCNAME 
LEFT JOIN SN_RACKDEV RD ON C.SERVER_SVRNUM = RD.RACKDEV_DEVNUM
LEFT JOIN (
	Select RACK_NUM, RACK_LOGICROOMNUM From SN_RACK Where RACK_STATUS != 'DEL'
) R ON RD.RACKDEV_RACKNUM = R.RACK_NUM 
LEFT JOIN (
	Select LOGICROOM_NUM From SN_LOGICROOM Where LOGICROOM_STATUS != 'DEL'
) L ON R.RACK_LOGICROOMNUM = L.LOGICROOM_NUM 
LEFT JOIN (
	Select INTRANETIP_DEVNUM From SN_INTRANETIP Where INTRANETIP_STATUS != 'DEL'
) E ON E.INTRANETIP_DEVNUM = A.VIP_NLBNUM 
LEFT JOIN (
	Select INTRANETIP_DEVNUM From SN_INTRANETIP Where INTRANETIP_STATUS != 'DEL'
) NW ON NW.INTRANETIP_DEVNUM = A.VIP_VIPNUM 
LEFT JOIN (
	Select INTERNETIP_BUSINESSNUM From SN_INTERNETIP Where INTERNETIP_STATUS != 'DEL'
) GW ON GW.INTERNETIP_BUSINESSNUM = A.VIP_VIPNUM 
荖司机 2018-04-10
  • 打赏
  • 举报
回复
我觉得你是不是可以换个思路,先针对A.B 两个表将数据过滤出来,插入临时表 TEMP, 然后拿该表再和其它表格进行join
荖司机 2018-04-10
  • 打赏
  • 举报
回复
我觉得你是不是可以换
  • 打赏
  • 举报
回复
引用 20 楼 hemowolf 的回复:
瞎说几点建议啊: 1、把 where 后面的条件放到前面去,变成各个表的子查询 2、把 != 'DEL' 的条件全部放到 On 的最前面,先进行判断

SELECT COUNT(*) FROM 
(Select ROW_ID, VIP_NLBNUM, VIP_VIPNUM From SN_VIPLOADBALANCE Where A.VIP_NLBNUM != '' AND A.VIP_NLBNUM IS NOT NULL AND A.VIP_STATUS != 'DEL') A
LEFT JOIN (Select VIPSVR_ROWID, VIPSVR_NUM, VIPSVR_SVRNUM From SN_VIPLOADINFO_SERVER Where VIPSVR_SVRNUM IS NOT NULL And VIPSVR_STATUS != 'DEL')B ON B.VIPSVR_ROWID=A.ROW_ID
LEFT JOIN SN_VIPLOADINFO_HGP H ON H.VIPHGP_STATUS != 'DEL' And A.ROW_ID = H.VIPHGP_ROWID AND B.VIPSVR_NUM = H.VIPHGP_NUM 
LEFT JOIN SN_HOSTGROUP J ON J.HOSTGROUP_STATUS != 'DEL' And H.VIPHGP_HGPNUM = J.HOSTGROUP_NUM
LEFT JOIN SN_SERVER C ON C.SERVER_STATUS != 'DEL' And C.SERVER_NUM = B.VIPSVR_SVRNUM 
LEFT JOIN SN_SERVER_HISTORY I I.OPERATETYPE = 'DEL' And ON I.SVRNUM = B.VIPSVR_SVRNUM
LEFT JOIN SN_SYSTEM K ON K.SYSTEM_STATUS != 'DEL' And I.SYSCNAME = K.SYSTEM_FCNAME
LEFT JOIN SN_RACKDEV RD ON C.SERVER_SVRNUM = RD.RACKDEV_DEVNUM
LEFT JOIN SN_RACK R ON R.RACK_STATUS != 'DEL' And RD.RACKDEV_RACKNUM = R.RACK_NUM
LEFT JOIN SN_LOGICROOM L ON L.LOGICROOM_STATUS != 'DEL' And R.RACK_LOGICROOMNUM = L.LOGICROOM_NUM 
LEFT JOIN SN_INTRANETIP E ON E.INTRANETIP_STATUS != 'DEL' And E.INTRANETIP_DEVNUM = A.VIP_NLBNUM 
LEFT JOIN SN_INTRANETIP NW ON NW.INTRANETIP_STATUS != 'DEL' And NW.INTRANETIP_DEVNUM = A.VIP_VIPNUM 
LEFT JOIN SN_INTERNETIP GW ON GW.INTERNETIP_STATUS != 'DEL' And GW.INTERNETIP_BUSINESSNUM = A.VIP_VIPNUM 
3、或者再试试把各个表单独的条件放到表中,作为一个子查询,on 后面只放与连接有关的条件

SELECT COUNT(*) 
FROM 
(Select ROW_ID, VIP_NLBNUM, VIP_VIPNUM From SN_VIPLOADBALANCE Where VIP_NLBNUM != '' AND VIP_NLBNUM IS NOT NULL AND VIP_STATUS != 'DEL') A
LEFT JOIN (
	Select VIPSVR_ROWID, VIPSVR_NUM, VIPSVR_SVRNUM From SN_VIPLOADINFO_SERVER Where B.VIPSVR_STATUS != 'DEL' And VIPSVR_SVRNUM IS NOT NULL
)B ON B.VIPSVR_ROWID = A.ROW_ID
LEFT JOIN (
	Select VIPHGP_ROWID, VIPHGP_NUM, VIPHGP_HGPNUM From SN_VIPLOADINFO_HGP Where VIPHGP_STATUS != 'DEL'
)H ON A.ROW_ID = H.VIPHGP_ROWID AND B.VIPSVR_NUM = H.VIPHGP_NUM
LEFT JOIN (
	Select HOSTGROUP_NUM From SN_HOSTGROUP Where HOSTGROUP_STATUS != 'DEL'
)J ON H.VIPHGP_HGPNUM = J.HOSTGROUP_NUM 
LEFT JOIN (
	Select SERVER_NUM From SN_SERVER Where SERVER_STATUS != 'DEL'
) C ON C.SERVER_NUM = B.VIPSVR_SVRNUM 
LEFT JOIN (
	Select SVRNUM, SYSCNAME From SN_SERVER_HISTORY Where OPERATETYPE = 'DEL'
)I ON I.SVRNUM = B.VIPSVR_SVRNUM 
LEFT JOIN (
	Select SYSTEM_FCNAME From SN_SYSTEM Where SYSTEM_STATUS != 'DEL'
)K ON I.SYSCNAME = K.SYSTEM_FCNAME 
LEFT JOIN SN_RACKDEV RD ON C.SERVER_SVRNUM = RD.RACKDEV_DEVNUM
LEFT JOIN (
	Select RACK_NUM, RACK_LOGICROOMNUM From SN_RACK Where RACK_STATUS != 'DEL'
) R ON RD.RACKDEV_RACKNUM = R.RACK_NUM 
LEFT JOIN (
	Select LOGICROOM_NUM From SN_LOGICROOM Where LOGICROOM_STATUS != 'DEL'
) L ON R.RACK_LOGICROOMNUM = L.LOGICROOM_NUM 
LEFT JOIN (
	Select INTRANETIP_DEVNUM From SN_INTRANETIP Where INTRANETIP_STATUS != 'DEL'
) E ON E.INTRANETIP_DEVNUM = A.VIP_NLBNUM 
LEFT JOIN (
	Select INTRANETIP_DEVNUM From SN_INTRANETIP Where INTRANETIP_STATUS != 'DEL'
) NW ON NW.INTRANETIP_DEVNUM = A.VIP_VIPNUM 
LEFT JOIN (
	Select INTERNETIP_BUSINESSNUM From SN_INTERNETIP Where INTERNETIP_STATUS != 'DEL'
) GW ON GW.INTERNETIP_BUSINESSNUM = A.VIP_VIPNUM 
把where之后的条件放前面,查询的结果就会少。
  • 打赏
  • 举报
回复
都是全表扫描 不想改变语句 就只能在索引上优化了看你!=的条件都是状态 这些字段都加索引 还有count(*)=>count(1)
surl61240 2018-04-10
  • 打赏
  • 举报
回复
把条件分散到子查询里面去,缩小每个子查询结果集的数量
jiajing1990_ 2018-04-09
  • 打赏
  • 举报
回复
千万不要用where 1=1 这样意味着你所有的索引都不会起作用的
75闪光雷 2018-04-09
  • 打赏
  • 举报
回复
引用 4 楼 hjgzj 的回复:
顶一下,别沉了
试试三楼说的 把你的查询条件 建立个索引 应该能加快速度
试着去做 2018-04-09
  • 打赏
  • 举报
回复
最后面的过滤条件只有A表和B表的,其它关联是不是可以去掉~!只留A表和B表的关联应该就可以了吧,你这只要统计数量,不用取其它表的字段,感觉应该可以去掉,你可以试试~
hcz600 2018-04-09
  • 打赏
  • 举报
回复
数据库如何实现
  • 打赏
  • 举报
回复
顶一下,别沉了
加载更多回复(12)

56,803

社区成员

发帖
与我相关
我的任务
社区描述
MySQL相关内容讨论专区
社区管理员
  • MySQL
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧