跟着项目学sql——查询语句优化(一)

现阶段,笔者的环境中只有SqlServer和Oracle。所以后面的文章更多的会以SqlServer的背景来做了。 

来看这样一张表WorkLink:

WorkLink表结构

跟着项目学sql——查询语句优化(一)_第1张图片

WorkLink表数据,现在表中有超过150W条数据,并持续增长中。

跟着项目学sql——查询语句优化(一)_第2张图片

跟着项目学sql——查询语句优化(一)_第3张图片

 一)现在需要实现分页查询功能,并且必须显示总记录数,如下:

在获取分页数据之前,需要我们先去获取总数 

select count(*) from WorkLink
或者select count(1) from WorkLinK
或者select count(id) from WorkLink

考虑到覆盖索引,这几种写法在服务器上执行速度差别不大,而且结果都一样,没有网络传输速度方面的因素,比较的意义不大。

 

合计175W多条数据,用时可以忽略不计,然后是分页查询语句,SqlServer上也有类似mysql中分页查询limit to的语句

offset fetch next

select *from WorkLink order by CreateDate desc offset 90000 rows fetch next 10 rows only

跟着项目学sql——查询语句优化(一)_第4张图片

用时2s仅仅是为了获取10条数据,显然是不可接受的。索引,在CreateDate列上创建索引速度就快了。同MySql innodb,SqlServer中的主键索引默认是聚集索引,

跟着项目学sql——查询语句优化(一)_第5张图片

 但不同的是可以在创建表时声明为非聚集索引,如下

 PRIMARY KEY UNCLUSTERED ([Id] ASC)

 这样的设计很灵活,但是用到的地方很少,可以当做没有这个设定。就比如这里的CreateDate,将Id列改为非聚集索引,并在CreateDate列创建聚集索引是不现实的,这很容易违反第一课——范式。这里的Id列作为主键是不可替代的。

 1)CreateDate列并不唯一,插入时间可能重复,没有创建聚集索引的资格。

跟着项目学sql——查询语句优化(一)_第6张图片

这里的按插入时间倒序指的正是按聚集索引倒序,因为用了自增Id作为主键,所以直接按Id倒序就可以了

select *from WorkLink order by Id desc offset 90000 rows fetch next 10 rows only

跟着项目学sql——查询语句优化(一)_第7张图片

现在耗时也可以忽略不计了。

2)CreateDate可能重复也直接导致用该列做排序会导致排序不稳定。为了保证排序准确性,需要加入如下条件

select *from WorkLink order by CreateDate desc,order by Id desc offset 90000 rows fetch next 10 rows only

到这里sql经过优化还是要把【order by CreateDate】这句去掉,直接用Id排序。

两种思路,殊途同归,因为默认排序(物理顺序)本就是聚集索引,所以只用一个Id就摆平了。

现在,回头看下第一步求表中数据的总行数的方法,表中数据的总行数不正就是聚集索引的叶子数么?

在SqlServer中的系统视图中找到sys.sysindexes,这个系统视图中的rowcnt字段记录了聚集索引的行数。

数据库中的每个索引和表在表中各占一行。该表存储在每个数据库中。执行查询语句

select rowcnt from sysindexes where id = object_id('WORKLINK') and indid=1--限定聚集索引

select count(*) as rowcnt from WORKLINK

结果是相同的 

跟着项目学sql——查询语句优化(一)_第8张图片

 针对未分区的表和索引,这个视图中的rowcnt字段是可信的,印证了前面的想法。当然,这条sql本身的应用价值不大,因为实际运用中,我们需要的总数往往是经过多条件查询后的总数,这个字段真的就只是系统用的。况且官方反复强调

虽然在sql server2000 的版本中就已经有了这个警告,虽然刚刚笔者用的版本是2014

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二)报表导出问题

1)导出全表数据 

      有这种场景的话,一般是针对一个人使用者来说的,就是说这个功能开发出来,用户只有一个。不然的话,全表数据取出来的话,占用的是服务器的内存,如果同一时间有几百几千个用户同时使用这个导出功能,会用掉好几G的内存。所以缓存是必须的,在同一时间段内,用户只用同一块内存,那么对于sql来说,用户就是这一块内存了。表=》缓存=》网络=》用户。

2)按条件导出数据

现在,需要添加导出功能,管理员可以在分页列表上进行跨页选择,并将选中的数据导出到Excel。

初始设计如下,管理员通过复选框勾选数据,点击导出按钮将所有选中的行导出。

跟着项目学sql——查询语句优化(一)_第9张图片

select *from WorkLink where id in(1,2,3,4,5,6......)

不同于Oracle的in只有1000个参数这个限制,SqlServer中的in的上限是个很大的数字,而且管理员一次操作勾选数万条的情况在设计时被当成违规操作处理了,这个情况同样适用于MySql。

看来只有在Oracle中才会把in当成个必要的事儿来处理,参数每几百个分一组用join或者or连接一下的吧。

 这里的问题在于:

I、全选按钮的勾选到底是全部选中还是选中本页?

II、复选框勾选代表添加,反选代表删除,就相当于将添加和删除的功能做在同一个按钮上了,这是很有歧义的。

III、与列表的检索功能冲突,每次检索后,之前的勾选就失效了。

 跨页多选还是用穿梭框的形式,将添加与删除分开,才不会引起歧义。

跟着项目学sql——查询语句优化(一)_第10张图片

     所以这仅仅是一个界面功能设计问题。

 

 

你可能感兴趣的:(sql,跟着项目学sql)