MSSQL参数消耗更多时间

时间:2013-08-29 08:09:23

标签: c# sql sql-server performance

我有一个数据库,其中一个表有大约2.500.000条记录。我正在获取大约150.000条记录,其中有2个不同的查询要进行测试。第一个返回30秒到1分钟之间的结果。但第二个,在3-4分钟之间作出反应,这非常奇怪。唯一改变的是第一个不使用参数但第二个没有。我是从C#运行的。对于安全问题,我想使用参数化但我无法理解为什么需要花费这么多时间。任何帮助将不胜感激。

首先查询:

        DECLARE @page INT=3
        DECLARE @pagesize INT=300
            string sql = "SELECT  Col1,Col2,Col3 FROM 
    (SELECT ROW_NUMBER() OVER(ORDER BY Col1) AS rownumber,Col1,Col2,Col3";
            sql += " FROM my_table WHERE  Col1 LIKE '" + letter + "%')  as somex 
WHERE  rownumber >= (@page-1)*(@pagesize)";
            sql += "AND rownumber <=(@page)*@pagesize; 
SELECT COUNT(*) FROM my_table WHERE col1 LIKE '" + letter + "%'";

第二次查询:

    DECLARE @page INT=3
    DECLARE @pagesize INT=300
    DECLARE @starting VARCHAR(10)='be'
        string sql = "SELECT  Col1,Col2,Col3FROM   
(SELECT ROW_NUMBER() OVER(ORDER BY Col1) AS rownumber,Col1,Col2,Col3";
        sql += " FROM my_table WHERE  Col1 LIKE @letter+'%')  as somex  
WHERE  rownumber >= (@page-1)*(@pagesize)";
        sql += "AND rownumber <=(@page)*@pagesize; SELECT COUNT(*) 
FROM my_table WHERE col1 LIKE @letter+'%'";

我的服务器是16GB Ram,4个真正的4个虚拟CPU,Sata磁盘。

编辑: Col1是群集和非群集索引。

进度:事实证明这些查询在另一台服务器上运行良好。但这让我更加困惑。可能是SQL Server的一些设置吗?

1 个答案:

答案 0 :(得分:2)

正如我在评论中所说,这听起来像parameter sniffing,但为了有所帮助,我想我会扩展它。网上有很多文章 比我更详细的细节,但参数嗅探的长期和短期是SQL-Server根据参数的值缓存执行计划,该值不会产生当前值的最佳执行计划。

假设Col1有一个非聚集索引,但不包括col2col3作为非关键列,那么 SQL-Server有两个选项,它可以在My_Table上执行聚簇索引扫描以获取Col1 LIKE @letter+'%'所有的行,或者它可以在Col1上搜索索引然后执行书签查找在聚集索引上获取值 对于索引返回的每一行。基于估计的行数,SQL-Server在两者之间切换的时候我记不清了我的头脑,它的百分比相当低,所以我很确定如果你要返回150,000条记录 在2,500,000中,优化器将进行聚簇索引扫描。但是,如果您只返回几百行,则最好使用书签查找。

当您不使用参数时,SQL-Server将在每次执行时创建一个新的执行计划,并为您生成该参数的最佳执行计划(假设您的统计信息是最新的),当您使用参数时他们第一次查询运行sql-server创建一个计划 基于该特定参数值,并存储计划供以后使用。每次运行查询时,sql-server都会识别出查询是相同的,因此不会重新编译它。这意味着,如果第一次运行查询,那就是 返回少量行的参数将存储书签查找计划。然后,如果下次运行查询,则会传递一个值,该值返回大量行,其中最佳计划是聚簇索引扫描,然后仍使用次优书签查找执行查询 将导致更长的执行时间。当然,这反过来也可能是相反的。有很多方法可以解决参数嗅探,但由于你的查询不是很复杂,编译时间不会很大,特别是与你说这个查询占用的30秒相比 即使在最佳状态下运行,我也会使用OPTION RECOMPILE Query hint

SELECT  Col1, Col2, Col3
FROM    (   SELECT  ROW_NUMBER() OVER(ORDER BY Col1) AS rownumber,Col1,Col2,Col3
            FROM    my_table 
            WHERE   Col1 LIKE @letter+'%'
        )  as somex  
WHERE   rownumber >= (@page-1)*(@pagesize)
AND     rownumber <= (@page) * @pagesize
OPTION (RECOMPILE); 

SELECT  COUNT(*) 
FROM    my_table 
WHERE   Col1 LIKE @letter+'%'
OPTION (RECOMPILE);

当您在新服务器上尝试执行此操作时,它执行正常的原因是,第一次在新服务器上运行时,必须编译参数化查询,并且生成的计划适合于提供的参数的值

最后一点,如果您使用的是SQL_Server 2012,则可以使用OFFSET/FETCH进行分页:

SELECT  Col1, Col2, Col3
FROM    My_table
WHERE   Col1 LIKE @letter+'%'
ORDER BY Col1 OFFSET (@page-1) * (@pagesize) ROWS FETCH NEXT @pagesize ROWS ONLY;