问题描述
有一数据表(产品标签表,每个产品一个唯一的SN,每月100万左右),查询界面上有2个联动下拉框,【规格】____,【批次】______
用户选择一个规格后(目前200来个规格),列出该规格下达过的生产计划的批次。
原有方式
规格列表
select 规格 from 标签表 group by 规格
根据规格获取批次
select 批次 From 标签表 Where 规格=‘某一规格’ group by 批次
由于要进行全表扫描,当数据到50万以上时速度就明显慢下来了,而到200万时上面任意一个查询都要1分钟左右。
解决方案
方案1:建立索引
组合“规格,批次”建立一个索引(批次单独建立了聚集索引)。
问题,因为一条记录对应一个索引条目,所以这个方案需要很多额外的空间,另外原来表上已经有3个索引了,过多的索引会导致更新与插入性能下降,并且死锁风险会提高,作为流水线上使用的模块,性是有要求的。
方案2:建立一张规格批次对应表,记录规格跟批次的对关系
由于规格200来个并且比较稳定,批次一月也就100来个,而且这100个批次只分配给二三十个批次所以这两者的组合一个月也就1500-2000条左右。
考虑使用规格批次对应表后,进一步就是确定怎么维护对应表的数据,
方式一,就是每次生产任务分解生成标签的同时更新规格批次对应表(即标签表记录的增删改时做对应的操作)。考虑那个任务分解代码已经够纠结了,不打算大量修改程序代码。
方式二,数据库建立个作业,定期增量更新
生产任务会做调整(删除或更改),不过变动几率不高,而且多数改动都在上班时间内任务下达后1-2小时内修改(隔天的基本已经在执行状态或已经完成了)
所以作业安排在晚上12点进行,而作业执行点之后标签表新增记录的规格与批次的对应关系则没包括在对应表中,因此下拉框的查询结果来自两部,作业执行点前的规格批次对应关系来自对应表,而作业执行点后规格批次对应关系则直接查询标签表,由于每次作业执行点都记录当前统计时最大的记录ID号,因此查询标签表时会使用如下的查询语句:
Select 批次 From 标签表 Where 规格='某一规格' And Id>xxxx ,由于在Id上建了索引,而每天心记录在3,4万条,所以这个查询在执行时间上基本稳定。
完成的代码类似下面:
ALTER Proc [dbo].[Get批次By规格] @规格 nvarchar(20) As Create Table #t_CT_BNO_MAP_tmp ( 批次 nvarchar(20) ) Declare @MaxLblId bigint Declare @SQL nvarchar(2000) Set @MaxLblId=0 select @MaxLblId= Max(LblId) From 规格批次对应表 Set @SQL= ' select 批次 From ( Select 批次 from 标签表 where Id>='+cast(@MaxLbLId as nvarchar(50))+' And 规格='''+@规格+''' group by 批次 Union Select 批次 From 规格批次对应表 where 规格='''+ @规格+''' ) as t group by 批次' Insert Into #t_CT_BNO_MAP_tmp Exec(@SQL) Select * from #t_CT_BNO_MAP_tmp Drop Table #t_CT_BNO_MAP_tmp
注意点:
上面采用了动态SQL来执行包含" Id>='+cast(@MaxLbLId as nvarchar(50))+" 的语句来获取标签表中的规格批次对应关系,如果不采用动态SQL,直接使用
Id>@MaxLblId的条件,那么由于是存储过程MSSQL查询优化器不清楚MaxLblId可能是多少,而忽略Id上已建的索引,而进行全表扫描。
两者的执行过程如下图:
(静态SQL语句)
(动态SQL语句)
作业任务代码:
declare @MaxLblId bigint Declare @SQL nvarchar(2000) Set @MaxLblId=0 select @MaxLblId= Max(LblId) From dbo.规格批次表 Set @SQL=' Insert Into 规格批次表(规格,batno,LblId) select 规格,Batno,LblId From ( Select 规格,batno Batno,Max(Id) LblId from 标签表 where Id>='+cast(@MaxLbLId as nvarchar(50))+' group by 规格, batno Except Select 规格,Batno,LblId From KB_Lable_CT_BNO_MAP ) as t ' Exec(@SQL)