当前位置: 代码迷 >> Sql Server >> 帮忙优化一个sql指令
  详细解决方案

帮忙优化一个sql指令

热度:27   发布时间:2016-04-24 10:27:11.0
求助帮忙优化一个sql指令
现在有一个企业表 qyml (id,qymc,flag),数据3万条
有一个产品表 spzs (id,cpmc,gsid,flag,picurl,jysm,xxsm),数据 120万条

现在有一个这样的需求,随机获取20条产品信息,每个公司的产品只能出现一个,且符合全文检索的关键字,我是这样写的指令

select top 20 * from (select a.id,GsID,Qymc,Cpmc,picurl,ROW_NUMBER() over (partition by gsid order by newid()) as tempRowID from Spzs a left join qyml b on a.GsID=b.id where a.flag=1 and b.flag=0 and b.enddate>getdate() and picurl is not null and picurl<>'' and gsid not in (0,209696,209696,60893,56128,54278,48227,48227,6476,58777,58397,56125,54943,54088,52846,322,206271,207286,62609,61985,57718,56238,16702,1619,610,191,178,211452,211132,205393,170022,70667,63266,62782,61816,61688,61203,59499,59014,58474,56629,56398,56134,54887,54194,1393,209)  and (contains((cpmc,jysm,xxsm),'白板') or contains((cpmc,jysm,xxsm),'信息化'))) a where tempRowID=1 order by NEWID()


我的基本思路是这样的,从spzs和qyml中联合查询,挑出符合条件的,且之前程序中没出现过的公司的其中随机一条数据取20个

只是这个指令执行起来效率是相当的慢,基本上需要10秒到30秒,比较耗费资源的就是 order by newid() 和 contains,可不用这两个又不知道该怎么实现了,求大能们帮帮忙

------解决方案--------------------
有全文索引?不好办。你可以尝试将子查询的记录写入临时表试试。
------解决方案--------------------
引用:
select top 10 * from (
select 
a.id,GsID,Qymc,Cpmc,picurl,ROW_NUMBER() over (partition by gsid order by newid()) as tempRowID
from Spzs a left join qyml b 
on a.GsID=b.id 
where a.flag=1 and b.flag=0 and b.enddate>getdate() and picurl is not null and picurl<>'' 
and gsid not in (0,209696,209696,60893,56128,54278,48227,48227,6476,58777,58397,56125,54943,54088,52846,322,206271,207286,62609,61985,57718,56238,16702,1619,610,191,178,211452,211132,205393,170022,70667,63266,62782,61816,61688,61203,59499,59014,58474,56629,56398,56134,54887,54194,1393,209)  
and (contains((cpmc,jysm,xxsm),'白板') or contains((cpmc,jysm,xxsm),'信息化'))
) a 
where tempRowID=1 order by NEWID()

指令太长了,从新粘贴一下

也建议将子查询写入临时表中,然后在临时表中再来操作
------解决方案--------------------
(contains((cpmc,jysm,xxsm),'白板') or contains((cpmc,jysm,xxsm),'信息化'))
-->这个可以这样
contains((cpmc,jysm,xxsm),'"白板" OR "信息化"') 


许多字段都没标记前缀,根本不只到那个表的字段,怎么做优化?

看你的意思是每次20个这样的取,应该考虑用临时表:
先将各自符合条件的记录选入 #a、#b,#a 中的 NEWID() 一次性生成;
删除不符合关联条件的记录;
#a 中取 NEWID() 的前20个,再到 b# 中与这20个关联的记录进行 NEWID() 选取,返回数据;
把这20个相关的数据都删除。

------解决方案--------------------
我4楼说的是中文啊,怎么不睬?
1)contains 用组合条件,查一遍总比查两遍快。
2)不要用 NOT IN!
你的 NOT IN 范围会越来越大,过滤的越多性能反而更低。
不要把多次取数作为独立需求处理,要把一串取数作为整体需求处理。
除了第一次需要生成大结果比较慢,每次只需要取20条并从临时表中删除,绝对能秒取。
3)第一次需要生成大结果时就决定了企业“随机”的次序,每次只需要按照这个次序取前20条即可。
4)关于每企业“随机”选一个产品
a.可以在第一次需要生成大结果时就“随机”选一个产品;
b.也可以在每次取20条企业时再分别“随机”选一个产品。
你需要实测一下哪个方案快。
------解决方案--------------------
select top 10 * from (
select 
a.id,GsID,Qymc,Cpmc,picurl,ROW_NUMBER() over (partition by gsid order by newid()) as tempRowID
from Spzs a left join qyml b 
on a.GsID=b.id 
where a.flag=1 and b.flag=0 and b.enddate>getdate() and picurl is not null and picurl<>'' 
and gsid not in (0,209696,209696,60893,56128,54278,48227,48227,6476,58777,58397,56125,54943,54088,52846,322,206271,207286,62609,61985,57718,56238,16702,1619,610,191,178,211452,211132,205393,170022,70667,63266,62782,61816,61688,61203,59499,59014,58474,56629,56398,56134,54887,54194,1393,209)  
and (contains((cpmc,jysm,xxsm),'白板') or contains((cpmc,jysm,xxsm),'信息化'))
) a 
where tempRowID=1 order by NEWID()


第一个问题:为什么随机取数据要在数据库中来做?
可不可以:1、程序实现 2、通过生成10个随机ID,然后再根据ID去锁定10条数据?
第二个问题:not in里的内容太多,为何不换个写法?
------解决方案--------------------
還是從篩分方面入手,繼續篩分SQl語句:

declare @tb table
(
  pid int ,
  cid int ,
  qymc nvarchar(100) ,
  cpmc nvarchar(100) ,
  picurl nvarchar(200)
)
insert into @tb select a.ID,GsID,Qymc,Cpmc,picurl
                        from Spzs a 
                                left join Qyml b on a.GsID = b.ID
                        where a.flag = 1
                                and picurl is not null
  相关解决方案