最近在忙着优化集团公司的一个报表。优化完成后,报表查询速度由从半小时以上(甚至查不出)到秒查的质变。从修改 SQL 查询语句逻辑到决定创建存储过程实现,花了我 3 天多的时间,在此总结一下,希望对朋友们有帮助。

数据背景

首先项目是西门子中国在我司实施部署的 MES 项目,由于项目是在产线上运作(3 years+),数据累积很大。

在项目的数据库中,大概上亿条数据的表有 5 个以上,千万级数据的表 10 个以上,百万级数据的表,很多…

(历史问题,当初实施无人监管,无人监控数据库这块的性能问题。PS:我刚入职不久…)

不多说,直接贴西门子中国的开发人员在我司开发的 SSRS 报表中的 SQL 语句:

select distinct b.MaterialID as matl_def_id, c.Descript, case when right(b.MESOrderID, 12) < '001000000000' then right(b.MESOrderID, 9)
else right(b.MESOrderID, 12) end  as pom_order_id, a.LotName, a.SourceLotName as ComLot,
e.DefID as ComMaterials, e.Descript as ComMatDes, d.VendorID, d.DateCode,d.SNNote, b.OnPlantID,a.SNCUST
from
(select m.lotname, m.sourcelotname, m.opetypeid, m.OperationDate,n.SNCUST from View1 mleft join co_sn_link_customer as n on n.SNMes=m.LotNamewhere( m.LotName in (select val from fn_String_To_Table(@sn,',',1)) or (@sn) = '') and( m.sourcelotname in (select val from fn_String_To_Table(@BatchID,',',1)) or (@BatchID) = '')and (n.SNCust like '%'+ @SN_ext + '%' or (@SN_ext)='')
) a
left join
(select * from Table1 where SNType = 'IntSN'and SNRuleName = 'ProductSNRule'and OnPlantID=@OnPlant
) b on b.SN = a.LotName
inner join MMdefinitions as c on c.DefID = b.MaterialID
left join  Table1 as d on d.SN = a.SourceLotName
inner join MMDefinitions as e on e.DefID = d.MaterialID
where not exists (select distinct LotName, SourceLotName from ELCV_ASSEMBLE_OPS
where LotName = a.SourceLotName and SourceLotName = a.LotName
)
and (d.DateCode in (select val from fn_String_To_Table(@DCode,',',1)) or (@DCode) = '')
and (d.SNNote  like '%'+@SNNote+'%' or (@SNNote) = '')
and ((case when right(b.MESOrderID, 12) < '001000000000' then right(b.MESOrderID, 9)
else right(b.MESOrderID, 12) end) in (select val from fn_String_To_Table(@order_id,',',1)) or (@order_id) = '')
and (e.DefID in (select val from fn_String_To_Table(@comdef,',',1)) or (@comdef) = '')
--View1是一个嵌套两层的视图(出于保密性,实际名称可能不同),里面有一张上亿数据的表和几张千万级数据的表做左连接查询
--Table1是一个数据记录超过1500万的表

这个查询语句,实际上通过我的检测和调查,在 B/S 系统前端已无法查出结果,半小时,一小时...

因为我直接在 SQL 查询分析器查,半小时都没有结果。(原因是里面对一张上亿级数据表和 3 张千万级数据表做全表扫描查询)

不由感慨,西门子中国的素质(或者说责任感)就这样?下面说说我的分析和走的弯路(思维误区),希望对你也有警醒。

推荐一个艿艿写的 6000+ Star 的 SpringBoot + SpringCloud + Dubbo 教程的仓库:https://github.com/YunaiV/SpringBoot-Labs

探索和误区

首先相关表的索引,没有建全的,把索引给建上。

索引这步完成后,发现情况还是一样,查询速度几乎没有改善。后来想起相关千万级数据以上的表,都还没有建立表分区。于是考虑建立表分区以及数据复制的方案。

这里有必要说明下: 我司报表用的是一个专门的数据库服务器,数据从产线订阅而来。就是常说的“读写分离”。

如果直接在原表上建立表分区,你会发现执行表分区的事物会直接死锁。原因是:表分区操作本身会锁表,产线还在推数据过来,这样很容易“阻塞”,“死锁”。

我想好的方案是:建立一个新表(空表),在新表上建好表分区,然后复制数据过来。

正打算这么干。等等!我好像进入了一个严重的误区!

分析: 原 SQL 语句和业务需求,是对产线的数据做产品以及序列号的追溯,关键是查询条件里没有有规律的”条件”(如日期、编号),贸然做了表分区,在这里几乎没有意义!反而会降低查询性能!

好险!还是一步一步来,先做 SQL 语句分析。

对原 SQL 语句的分析

对原 SQL 语句的分析如下:

  • 查询语句的 where 条件,有大量 @var in … or (@var =”) 的片段。

  • where 条件有 like ‘%’+@var+’%’。

  • where 条件有 case … end 函数。

  • 多次连接同一表查询,另外使用本身已嵌套的视图表,是不是必须,是否可替代?

  • SQL 语句有号,视图中也有号出现。

优化设计

首先是用存储过程改写,好处是设计灵活。

核心思想是: 用一个或多个查询条件(查询条件要求至少输入一个)得到临时表,每个查询条件如果查到集合,就更新这张临时表,最后汇总的时候,只需判断这个临时表是否有值。

以此类推,可以建立多个临时表,将查询条件汇总。

这样做目前来看至少两点好处:

  • 省去了对变量进行 =@var or (@var=”)的判断。

  • 抛弃 SQL 拼接,提高代码可读性。

再有就是在书写存储过程,这个过程中要注意:

  • 尽量想办法使用临时表扫描替代全表扫描。

  • 抛弃 in 和 not in 语句,使用 exists 和 not exists 替代。

  • 和客户确认,模糊查询是否有必要,如没有必要,去掉 like 语句。

  • 注意建立适当的,符合场景的索引。

  • 踩死 “*” 号。

  • 避免在 where 条件中对字段进行函数操作。

  • 对实时性要求不高的报表,允许脏读(with(nolock))。

推荐一个艿艿写的 3000+ Star 的 SpringCloud Alibaba 电商开源项目的仓库:https://github.com/YunaiV/onemall

存储过程

如果想参考优化设计片段的详细内容,请参阅 SQL 代码:

/*** 某某跟踪报表**/
--exec spName1 '','','','','','','公司代号'
CREATE Procedure spName1@MESOrderID nvarchar(320), --工单号,最多30个@LotName nvarchar(700),    --产品序列号,最多50个@DateCode nvarchar(500),   --供应商批次号,最多30个@BatchID nvarchar(700),    --组装件序列号/物料批号,最多50个@comdef nvarchar(700),     --组装件物料编码,最多30个@SNCust nvarchar(1600),    --外部序列号,最多50个@OnPlant nvarchar(20)      --平台
AS
BEGINSET NOCOUNT ON;/*** 1)定义全局的临时表,先根据六个查询条件的任意一个,得出临时表结果**/CREATE TABLE #FinalLotName(LotName NVARCHAR(50),       --序列号SourceLotName NVARCHAR(50), --来源序列号SNCust NVARCHAR(128)        --外部序列号)--1.1IF @LotName<>''BEGINSELECT Val INTO #WorkLot FROM fn_String_To_Table(@LotName,',',1)SELECT LotPK,LotName INTO #WorkLotPK FROM MMLots WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLot b WHERE b.Val=MMLots.LotID)--求SourceLotPK只能在这里求SELECT a.LotPK,a.SourceLotPK into #WorkSourcePK FROM MMLotOperations a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLotPK b WHERE b.LotPK=a.LotPK) AND a.SourceLotPK IS NOT NULLSELECT a.LotPK,a.SourceLotPK,b.LotName INTO #WorkSourcePK2 FROM #WorkSourcePK a JOIN #WorkLotPK b ON a.LotPK=b.LotPKINSERT INTO #FinalLotName SELECT a.LotName,b.LotName AS SourceLotName,NULL FROM #WorkSourcePK2 a JOIN (SELECT LotPK,LotName FROM MMLots WITH(NOLOCK) ) b on a.SourceLotPK=b.LotPK --b的里面加不加WHERE RowDeleted=0待确定SELECT a.LotName,a.SourceLotName,b.SNCust INTO #FinalLotNameX1 FROM #FinalLotName a LEFT JOIN CO_SN_LINK_CUSTOMER b WITH(NOLOCK) ON a.LotName=b.SNMesDELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #FinalLotNameX1END--1.2IF @BatchID<>''BEGINSELECT Val INTO #WorkSourceLot FROM fn_String_To_Table(@BatchID,',',1)IF EXISTS(SELECT 1 FROM #FinalLotName)--如果@LotName也不为空BEGINSELECT a.LotName,a.SourceLotName,a.SNCust INTO #FinalLotNameX2 FROM #FinalLotName a WHERE EXISTS(SELECT 1 FROM #WorkSourceLot b WHERE a.SourceLotName=b.Val)DELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #FinalLotNameX2ENDELSE --@LotName条件为空BEGINSELECT LotPK AS SourceLotPK,LotName AS SourceLotName INTO #2 FROM MMLots WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkSourceLot b WHERE b.Val=MMLots.LotID)SELECT a.LotPK,a.SourceLotPK into #21 FROM MMLotOperations a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #2 b WHERE b.SourceLotPK=a.SourceLotPK)SELECT a.LotPK,a.SourceLotPK,b.SourceLotName INTO #22 FROM #21 a JOIN #2 b ON a.SourceLotPK=b.SourceLotPKINSERT INTO #FinalLotName SELECT b.LotName,a.SourceLotName,NULL FROM #22 a JOIN (SELECT LotPK,LotName FROM MMLots WITH(NOLOCK) ) b on a.LotPK=b.LotPK --b的里面加不加WHERE RowDeleted=0待确定SELECT a.LotName,a.SourceLotName,b.SNCust INTO #FinalLotNameX21 FROM #FinalLotName a LEFT JOIN CO_SN_LINK_CUSTOMER b WITH(NOLOCK) ON a.LotName=b.SNMesDELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #FinalLotNameX21ENDEND--1.3IF @SNCust<>''BEGINSELECT Val INTO #WorkCustomSN FROM fn_String_To_Table(@SNCust,',',1)IF EXISTS(SELECT 1 FROM #FinalLotName)--前面两个条件至少有一个有值BEGINSELECT a.LotName,a.SourceLotName,a.SNCust INTO #FinalLotNameX3 FROM #FinalLotName a WHERE EXISTS(SELECT 1 FROM #WorkCustomSN b WHERE a.SNCust=b.Val)DELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #FinalLotNameX3ENDELSEBEGINSELECT a.SNMes INTO #WorkLotX FROM CO_SN_LINK_CUSTOMER a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkCustomSN b WHERE a.SNCust=b.Val)-------------------以下逻辑和变量1(@LotName)类似[先根据外部序列号求解序列号,再照搬第一个判断变量的方式]SELECT LotPK,LotName INTO #WorkLotPKX FROM MMLots WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLotX b WHERE b.SNMes=MMLots.LotID)--求SourceLotPK只能在这里求SELECT a.LotPK,a.SourceLotPK into #WorkSourcePKX FROM MMLotOperations a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLotPKX b WHERE b.LotPK=a.LotPK) AND a.SourceLotPK IS NOT NULLSELECT a.LotPK,a.SourceLotPK,b.LotName INTO #WorkSourcePK2X FROM #WorkSourcePKX a JOIN #WorkLotPKX b ON a.LotPK=b.LotPKINSERT INTO #FinalLotName SELECT a.LotName,b.LotName AS SourceLotName,NULL FROM #WorkSourcePK2X a JOIN (SELECT LotPK,LotName FROM MMLots WITH(NOLOCK) ) b on a.SourceLotPK=b.LotPK --b的里面加不加WHERE RowDeleted=0待确定SELECT a.LotName,a.SourceLotName,b.SNCust INTO #FinalLotNameX31 FROM #FinalLotName a LEFT JOIN CO_SN_LINK_CUSTOMER b WITH(NOLOCK) ON a.LotName=b.SNMesDELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #FinalLotNameX31-----------------------ENDEND/*** 2)定义全局的临时表,用于替换第一个全局临时表。**/CREATE TABLE #FinalCO_SN(SN NVARCHAR(50),SourceSN NVARCHAR(50),SNCust NVARCHAR(128),matl_def_id NVARCHAR(50),--sn的物料IDComMaterials NVARCHAR(50),  --SourceSN的物料IDMESOrderID NVARCHAR(20),OnPlantID NVARCHAR(20),VendorID NVARCHAR(20),DateCode NVARCHAR(20) ,SNNote NVARCHAR(512))--2.1IF @MESOrderID<>''BEGIN-------------------------------将MESOrderID做特殊处理-----------------------------------SELECT Val INTO #WorkMESOrderID FROM fn_String_To_Table(@MESOrderID,',',1)IF @OnPlant='Comba'BEGINUPDATE #WorkMESOrderID SET Val='C000'+Val WHERE LEN(Val)=9ENDELSEBEGINUPDATE #WorkMESOrderID SET Val='W000'+Val WHERE LEN(Val)=9ENDSELECT SN,MaterialID,MESOrderID,OnPlantID INTO #WorkCO_SN1 FROM CO_SN_GENERATION a WITH(NOLOCK)WHERE SNType='IntSN' AND SNRuleName = 'ProductSNRule' AND OnPlantID=@OnPlantAND EXISTS(SELECT 1 FROM #WorkMESOrderID b WHERE a.MESOrderID=b.Val)--------------------------------------------------------------------------------------------条件判断(逻辑分析)开始IF EXISTS(SELECT 1 FROM #FinalLotName)--如果前面判断的查询条件有值BEGIN--查出SourceLotName对应的查询字段SELECT a.SN AS SourceLotName,a.VendorID,a.DateCode,a.SNNote,a.MaterialID AS ComMaterials INTO #SourceLotNameTable FROM CO_SN_GENERATION a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.SourceLotName)INSERT INTO #FinalCO_SNSELECT a.LotName,a.SourceLotName,d.SNCust,b.MaterialID,c.ComMaterials,b.MESOrderID,b.OnPlantID,c.VendorID,c.DateCode,c.SNNote FROM #FinalLotName aLEFT JOIN #WorkCO_SN1 b ON a.LotName=b.SNLEFT JOIN #SourceLotNameTable c ON a.SourceLotName=c.SourceLotNameLEFT JOIN CO_SN_LINK_CUSTOMER d WITH(NOLOCK) ON a.LotName=d.SNMesENDELSEBEGIN--已知SN集合求解对应的SourceSN和SNCust集合------------------------------------------SELECT LotPK,LotName INTO #WorkLotPK410 FROM MMLots WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkCO_SN1 b WHERE b.SN=MMLots.LotID)SELECT a.LotPK,a.SourceLotPK into #WorkSourcePK420 FROM MMLotOperations a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLotPK410 b WHERE b.LotPK=a.LotPK) AND a.SourceLotPK IS NOT NULLSELECT a.LotPK,a.SourceLotPK,b.LotName INTO #WorkSourcePK430 FROM #WorkSourcePK420 a JOIN #WorkLotPK410 b ON a.LotPK=b.LotPKINSERT INTO #FinalLotName SELECT a.LotName,b.LotName AS SourceLotName,NULL FROM #WorkSourcePK430 a JOIN (SELECT LotPK,LotName FROM MMLots WITH(NOLOCK) ) b on a.SourceLotPK=b.LotPK --b的里面加不加WHERE RowDeleted=0待确定SELECT a.LotName,a.SourceLotName,b.SNCust INTO #FinalLotNameX440 FROM #FinalLotName a LEFT JOIN CO_SN_LINK_CUSTOMER b WITH(NOLOCK) ON a.LotName=b.SNMesDELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #FinalLotNameX440-------------------------------------------------------------------------------------SELECT a.SN AS SourceLotName,a.VendorID,a.DateCode,a.SNNote,a.MaterialID AS ComMaterials INTO #SourceLotNameTable2 FROM CO_SN_GENERATION a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.SourceLotName)INSERT INTO #FinalCO_SNSELECT a.LotName,a.SourceLotName,a.SNCust,b.MaterialID,c.ComMaterials,b.MESOrderID,b.OnPlantID,c.VendorID,c.DateCode,c.SNNote FROM #FinalLotName aLEFT JOIN #WorkCO_SN1 b ON a.LotName=b.SNLEFT JOIN #SourceLotNameTable2 c ON a.SourceLotName=c.SourceLotNameENDEND--2.2IF @DateCode<>''BEGINSELECT Val INTO #WorkDateCode FROM fn_String_To_Table(@DateCode,',',1)--此@DataCode条件求解出来的是SourceSNSELECT SN AS SourceSN,MaterialID AS ComMaterials,VendorID,DateCode,SNNote INTO #WorkSourceSNT1 FROM CO_SN_GENERATION a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkDateCode b WHERE a.DateCode=b.Val)------------------------------------------------------------------------------------------------------条件判断(逻辑分析)开始IF EXISTS(SELECT 1 FROM #FinalCO_SN)--如果前面判断的查询条件有值BEGINSELECT a.LotName,a.SourceLotName,a.SNCust,a.MaterialID,a.ComMaterials,a.MESOrderID,a.OnPlantID,a.VendorID,a.DateCode,a.SNNote INTO #TMP51 FROM #FinalCO_SN a WHERE EXISTS (SELECT 1 FROM #WorkDateCode b WHERE a.DateCode=b.Val)DELETE FROM #FinalCO_SNINSERT INTO #FinalCO_SN SELECT LotName,SourceLotName,SNCust,MaterialID,ComMaterials,MESOrderID,OnPlantID,VendorID,DateCode,SNNote FROM #TMP51ENDELSEBEGINIF EXISTS(SELECT 1 FROM #FinalLotName)BEGIN--查出SourceLotName对应的查询字段SELECT a.SourceSN,a.VendorID,a.DateCode,a.SNNote,a.ComMaterials INTO #SourceLTX5 FROM #WorkSourceSNT1 a WHERE EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SourceSN=b.SourceLotName)--查出SN对应的查询字段SELECT SN,MaterialID,MESOrderID,OnPlantID INTO #WorkSNT510 FROM CO_SN_GENERATION a WITH(NOLOCK)WHERE SNType='IntSN' AND SNRuleName = 'ProductSNRule' AND OnPlantID=@OnPlantAND EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.LotName)INSERT INTO #FinalCO_SNSELECT a.LotName,a.SourceLotName,d.SNCust,b.MaterialID,c.ComMaterials,b.MESOrderID,b.OnPlantID,c.VendorID,c.DateCode,c.SNNote FROM #FinalLotName aLEFT JOIN #WorkSNT510 b ON a.LotName=b.SNLEFT JOIN #WorkSourceSNT1 c ON a.SourceLotName=c.SourceSNLEFT JOIN CO_SN_LINK_CUSTOMER d WITH(NOLOCK) ON a.LotName=d.SNMesENDELSEBEGIN--已知SourceSN集合求解对应的SN和SNCust集合------------------------------------------SELECT LotPK AS SourceLotPK,LotName AS SrouceLotName INTO #WorkLotX510 FROM MMLots WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkSourceSNT1 b WHERE b.SourceSN=MMLots.LotID)SELECT a.LotPK,a.SourceLotPK into #WorkLotX520 FROM MMLotOperations a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLotX510 b WHERE b.SourceLotPK=a.SourceLotPK)SELECT a.LotPK,a.SourceLotPK,b.SrouceLotName INTO #WorkLotX530 FROM #WorkLotX520 a JOIN #WorkLotX510 b ON a.SourceLotPK=b.SourceLotPKINSERT INTO #FinalLotName SELECT b.LotName,a.SrouceLotName,NULL FROM #WorkLotX530 a JOIN (SELECT LotPK,LotName FROM MMLots WITH(NOLOCK) ) b on a.LotPK=b.LotPK --b的里面加不加WHERE RowDeleted=0待确定SELECT a.LotName,a.SourceLotName,b.SNCust INTO #WorkLotX540 FROM #FinalLotName a LEFT JOIN CO_SN_LINK_CUSTOMER b WITH(NOLOCK) ON a.LotName=b.SNMesDELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #WorkLotX540-------------------------------------------------------------------------------------SELECT SN,MaterialID,MESOrderID,OnPlantID INTO #WorkLotX550 FROM CO_SN_GENERATION a WITH(NOLOCK)WHERE SNType='IntSN' AND SNRuleName = 'ProductSNRule' AND OnPlantID=@OnPlantAND EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.LotName)INSERT INTO #FinalCO_SNSELECT a.LotName,a.SourceLotName,a.SNCust,b.MaterialID,c.ComMaterials,b.MESOrderID,b.OnPlantID,c.VendorID,c.DateCode,c.SNNote FROM #FinalLotName aLEFT JOIN #WorkLotX550 b ON a.LotName=b.SNLEFT JOIN #WorkSourceSNT1 c ON a.SourceLotName=c.SourceSNENDENDEND--2.3IF @comdef<>''BEGINSELECT Val INTO #WorkComdef FROM fn_String_To_Table(@comdef,',',1)--此@comdef条件求解出来的是SourceSNSELECT SN AS SourceSN,MaterialID AS ComMaterials,VendorID,DateCode,SNNote INTO #WorkSourceSNT16 FROM CO_SN_GENERATION a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkComdef b WHERE a.MaterialID=b.Val)------------------------------------------------------------------------------------------------------条件判断(逻辑分析)开始IF EXISTS(SELECT 1 FROM #FinalCO_SN)--如果前面判断的查询条件有值BEGINSELECT a.LotName,a.SourceLotName,a.SNCust,a.MaterialID,a.ComMaterials,a.MESOrderID,a.OnPlantID,a.VendorID,a.DateCode,a.SNNote INTO #TMP516 FROM #FinalCO_SN a WHERE EXISTS (SELECT 1 FROM #WorkComdef b WHERE a.matl_def_id=b.Val)DELETE FROM #FinalCO_SNINSERT INTO #FinalCO_SN SELECT LotName,SourceLotName,SNCust,MaterialID,ComMaterials,MESOrderID,OnPlantID,VendorID,DateCode,SNNote FROM #TMP516ENDELSEBEGINIF EXISTS(SELECT 1 FROM #FinalLotName)BEGIN--查出SourceLotName对应的查询字段SELECT a.SourceSN,a.VendorID,a.DateCode,a.SNNote,a.ComMaterials INTO #SourceLTX56 FROM #WorkSourceSNT16 a WHERE EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SourceSN=b.SourceLotName)--查出SN对应的查询字段SELECT SN,MaterialID,MESOrderID,OnPlantID INTO #WorkSNT5106 FROM CO_SN_GENERATION a WITH(NOLOCK)WHERE SNType='IntSN' AND SNRuleName = 'ProductSNRule' AND OnPlantID=@OnPlantAND EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.LotName)INSERT INTO #FinalCO_SNSELECT a.LotName,a.SourceLotName,d.SNCust,b.MaterialID,c.ComMaterials,b.MESOrderID,b.OnPlantID,c.VendorID,c.DateCode,c.SNNote FROM #FinalLotName aLEFT JOIN #WorkSNT5106 b ON a.LotName=b.SNLEFT JOIN #WorkSourceSNT16 c ON a.SourceLotName=c.SourceSNLEFT JOIN CO_SN_LINK_CUSTOMER d WITH(NOLOCK) ON a.LotName=d.SNMesENDELSEBEGIN--已知SourceSN集合求解对应的SN和SNCust集合------------------------------------------SELECT LotPK AS SourceLotPK,LotName AS SrouceLotName INTO #WorkLotX5106 FROM MMLots WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkSourceSNT16 b WHERE b.SourceSN=MMLots.LotID)SELECT a.LotPK,a.SourceLotPK into #WorkLotX5206 FROM MMLotOperations a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #WorkLotX5106 b WHERE b.SourceLotPK=a.SourceLotPK)SELECT a.LotPK,a.SourceLotPK,b.SrouceLotName INTO #WorkLotX5306 FROM #WorkLotX5206 a JOIN #WorkLotX5106 b ON a.SourceLotPK=b.SourceLotPKINSERT INTO #FinalLotName SELECT b.LotName,a.SrouceLotName,NULL FROM #WorkLotX5306 a JOIN (SELECT LotPK,LotName FROM MMLots WITH(NOLOCK) ) b on a.LotPK=b.LotPK --b的里面加不加WHERE RowDeleted=0待确定SELECT a.LotName,a.SourceLotName,b.SNCust INTO #WorkLotX5406 FROM #FinalLotName a LEFT JOIN CO_SN_LINK_CUSTOMER b WITH(NOLOCK) ON a.LotName=b.SNMesDELETE FROM #FinalLotNameINSERT INTO #FinalLotName SELECT LotName,SourceLotName,SNCust FROM #WorkLotX5406-------------------------------------------------------------------------------------SELECT SN,MaterialID,MESOrderID,OnPlantID INTO #WorkLotX5506 FROM CO_SN_GENERATION a WITH(NOLOCK)WHERE SNType='IntSN' AND SNRuleName = 'ProductSNRule' AND OnPlantID=@OnPlantAND EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.LotName)INSERT INTO #FinalCO_SNSELECT a.LotName,a.SourceLotName,a.SNCust,b.MaterialID,c.ComMaterials,b.MESOrderID,b.OnPlantID,c.VendorID,c.DateCode,c.SNNote FROM #FinalLotName aLEFT JOIN #WorkLotX5506 b ON a.LotName=b.SNLEFT JOIN #WorkSourceSNT16 c ON a.SourceLotName=c.SourceSNENDENDEND/*** 3)条件判断结束**/IF EXISTS(SELECT 1 FROM #FinalLotName)BEGINIF EXISTS(SELECT 1 FROM #FinalCO_SN)BEGIN--3.1SELECT a.matl_def_id,b.Descript,a.MESOrderID AS pom_order_id,a.SN AS LotName,a.SourceSN AS ComLot,a.ComMaterials,c.Descript AS ComMatDes,a.VendorID,a.DateCode,a.SNNote,OnPlantID,SNCust FROM #FinalCO_SN aJOIN MMDefinitions b WITH(NOLOCK) ON a.matl_def_id=b.DefIDJOIN MMDefinitions c WITH(NOLOCK) ON a.ComMaterials=c.DefIDWHERE NOT EXISTS(select distinct SN, SourceSN from #FinalCO_SN xwhere x.SN = a.SourceSN and x.SourceSN = a.SN)ENDELSEBEGIN--3.2--3.2.1求解SN的必查字段SELECT SN,MaterialID,MESOrderID,OnPlantID INTO #FinalSNX1 FROM CO_SN_GENERATION a WITH(NOLOCK)WHERE SNType='IntSN' AND SNRuleName = 'ProductSNRule' AND OnPlantID=@OnPlantAND EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.LotName)--3.2.2求解SourceSN的必查字段SELECT a.SN AS SourceLotName,a.VendorID,a.DateCode,a.SNNote,a.MaterialID AS ComMaterials INTO #FinalSNX2 FROM CO_SN_GENERATION a WITH(NOLOCK) WHERE EXISTS(SELECT 1 FROM #FinalLotName b WHERE a.SN=b.SourceLotName)SELECT b.MaterialID AS matl_def_id,x.Descript,b.MESOrderID AS pom_order_id,b.SN AS LotName,c.SourceLotName AS ComLot,c.ComMaterials,y.Descript AS ComMatDes,c.VendorID,c.DateCode,c.SNNote,b.OnPlantID,a.SNCustFROM #FinalLotName aLEFT JOIN #FinalSNX1 b ON a.LotName=b.SNLEFT JOIN #FinalSNX2 c ON a.SourceLotName=c.SourceLotNameJOIN MMDefinitions x WITH(NOLOCK) ON b.MaterialID=x.DefIDJOIN MMDefinitions y WITH(NOLOCK) ON c.ComMaterials=y.DefIDWHERE NOT EXISTS(SELECT DISTINCT * FROM #FinalLotName zWHERE z.LotName=a.SourceLotName and z.SourceLotName=a.LotName)ENDENDELSEBEGINIF EXISTS(SELECT 1 FROM #FinalCO_SN)BEGIN--3.3SELECT a.matl_def_id,b.Descript,a.MESOrderID AS pom_order_id,a.SN AS LotName,a.SourceSN AS ComLot,a.ComMaterials,c.Descript AS ComMatDes,a.VendorID,a.DateCode,a.SNNote,OnPlantID,SNCust FROM #FinalCO_SN aJOIN MMDefinitions b WITH(NOLOCK) ON a.matl_def_id=b.DefIDJOIN MMDefinitions c WITH(NOLOCK) ON a.ComMaterials=c.DefIDWHERE NOT EXISTS(select distinct SN, SourceSN from #FinalCO_SN xwhere x.SN = a.SourceSN and x.SourceSN = a.SN)ENDELSEBEGIN--3.4PRINT 'There is no queryable condition,please enter at less a query conditon.'ENDENDEND
GO

虽然牺牲了代码的可读性,但创造了性能价值。本人水平有限,还请各位不吝赐教!

最后,将 SSRS 报表替换成此存储过程后,SQL 查询分析器是秒查的。B/S 前端用时 1~2 秒!

总结

平常的你是否偶尔会因急于完成任务而书写一堆性能极低的 SQL 语句呢?写出可靠性能的 SQL 语句不难,难的是习惯。

本文的优化思想很简单,关键点是避免全表扫描&注重 SQL 语句写法&索引。

另外,如果你查询的表有可能会在查询时段更新,而实际业务需求允许脏读,可加 with(nolock)预防查询被更新事物阻塞。

耗时 3 天,上亿数据如何做到秒级查询?相关推荐

  1. 耗时3天,上亿数据如何做到秒级查询?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:sohu.gg/jIp59N 最近在忙着优化集团公司的一个报 ...

  2. es对几十亿数据能达到秒级响应吗_万亿数据下的多维实时分析系统,如何做到亚秒级响应...

    导语 当业务发展到一定规模,实时数据仓库是一个必要的基础服务.从数据驱动方面考虑,多维实时数据分析系统的重要性也不言而喻.但是当数据量巨大的情况下,拿腾讯看点来说,一天上报的数据量达到万亿级的规模,要 ...

  3. 上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB

    面试题 & 真实经历 面试题:在数据量很大的情况下,怎么实现深度分页? 大家在面试时,或者准备面试中可能会遇到上述的问题,大多的回答基本上是分库分表建索引,这是一种很标准的正确回答,但现实总是 ...

  4. 查询a表有但是b表没有的数据_牛逼!一个上亿数据的报表竟然能做到秒查~

    数据背景 首先项目是西门子中国在我司实施部署的MES项目,由于项目是在产线上运作(3 years+),数据累积很大.在项目的数据库中,大概上亿条数据的表有5个以上,千万级数据的表10个以上,百万级数据 ...

  5. 实战上亿数据,如何实现秒查!

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 作者:J ...

  6. 前端如何展示一个有上亿数据的树结构

    需求描述 数据上亿,有数千个从属于根节点的一级子节点: 每个一级子节点有数个到数万个二级子节点: 二级子节点可能有三级子节点: 子节点的最多层深不定(即,也许可能有 10 层): 需求分析 当面临一个 ...

  7. 实战:上亿数据如何秒查

    作者:blog.csdn.net/chenleixing/article/details/44994571 最近在忙着优化集团公司的一个报表.优化完成后,报表查询速度有从半小时以上(甚至查不出)到秒查 ...

  8. 高效率批量插入上亿数据

    转至 : https://blog.csdn.net/xiyang_1990/article/details/78771962 这里提供一种方法,使用 APPEND 提示,使得十分钟内插入上亿数据成为 ...

  9. 双引擎驱动Quick BI十亿数据0.3秒分析,首屏展示时间缩短30%

    简介:在规划中,Quick BI制定了产品竞争力建设的三大方向,包括Quick(快)能力.移动端能力和集成能力.针对其中的产品"报表查看打开慢""报表开发数据同步慢&qu ...

最新文章

  1. 2021 - 9 下旬 数据结构-线性表-循环队列-java实现代码
  2. 【数据结构与算法-1】常用数据结构
  3. java 获取当前目录_java获得当前文件路径
  4. VS2008 Tips #008 如何创建ASP.NET Web 用户控件并包含在Web 页面中
  5. Test2 unit2+3
  6. 10小时,就能吃透Kafka源码?
  7. 西门子精彩SMART触摸屏下载程序时常见问题分析与处理对策
  8. G_O_R加速器仿真之计算原理
  9. 整理41个Python不同方面的练习题,希望对你有帮助
  10. MyBatis动态SQL
  11. element组件的表单验证手机号
  12. 幼儿园教学方案植树节
  13. 五分钟理解什么是面向对象
  14. Mybatis框架创建逆向工程步骤
  15. Qt 之 QDateEdit 和 QTimeEdit
  16. STM32定时器-6步PWM输出
  17. 名图怎么弄云服务器_“双车”战略,名图如何驱动新兴细分市场
  18. Class Proxy
  19. leetcode——回溯法基础
  20. 单片机0804AD转换c语言程序,模数转换芯片ADC0804的应用

热门文章

  1. 域滤波:方框、高斯、中值、双边滤波
  2. 压缩跟踪Compressive Tracking
  3. 数字图像处理:第十二章 小波变换
  4. Ubuntu上手动安装sbt
  5. Split in Java
  6. MySQL主从复制故障案例一
  7. The maximum string content length quota (8192) has been exceeded while reading XML data
  8. ipsec在企业网中的应用(IKE野蛮模式)
  9. linux环境下创建MyOS虚拟机
  10. 社保,交得越多亏得越多(转)