多条件查询及 left join 的利用,几乎在所有项目都会用到。但只有数据量大、并发大时才会有人去注意他的性能。

先来看一个最有代表性的分页存储过程:

ALTER PROCEDURE [dbo].[P_Article_List]  
@page int=1,
@pagesize int=10,
@key VARCHAR(450)=''
AS
BEGIN
    SELECT COUNT(1)OVER(PARTITION BY '''') AS Total,b.NickName,b.Avator,a.*
    FROM dbo.t_article a with (NOLOCK)
    LEFT join t_user b with (NOLOCK) ON b.UserID=a.UserID
    WHERE a.IsValid=1
    AND (@key='' or a.Title like '%'+@key+'%')
    ORDER BY a.order DESC desc
    OFFSET @pagesize*(@page-1) ROWS FETCH NEXT @pagesize ROWS ONLY
END

这是一个很标准的存储过程,这样写代码也很漂亮,但这样的性能很差。通过查看执行计划,你会发现一个很奇怪的问题:

明明是 left join,为什么执行计划里是 inner join 呢,要知道后者的性能是低很多的。

这个问题是因为 where 条件导致的,所以我们要优化它,就必须用新的方式:

先把左边表的数据按条件查询出来放入临时表,再将临时表与右表 left join,不带where

当然,还有一个很重要的因素,分页,我们可以把分页也在临时表里先做了,比如一页10条,这样最后 left join时,左表只有10条数据,这个性能,就会很高

最后贴出优化后的存储过程:

ALTER PROCEDURE [dbo].[P_Article_List]  
@page int=1,
@pagesize int=10,
@key VARCHAR(450)=''
AS
BEGIN
    SELECT COUNT(1)OVER(PARTITION BY '''') AS Total,b.NickName,b.Avator,a.* FROM
    (SELECT * FROM dbo.t_article with (NOLOCK)
        WHERE IsValid=1
        AND (@key='' or Title like '%'+@key+'%')
        ORDER BY order DESC desc
        OFFSET @pagesize*(@page-1) ROWS FETCH NEXT @pagesize ROWS ONLY
    ) a
    LEFT join t_user b with (NOLOCK) ON b.UserID=a.UserID
END

搞定,最后测试,我们数据库的这个存储过程,查询时间从平均 2.3 秒优化到了 0.15 秒!

标签: none

添加新评论