我有一个数据库,其中一个表有大约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的一些设置吗?
答案 0 :(得分:2)
正如我在评论中所说,这听起来像parameter sniffing,但为了有所帮助,我想我会扩展它。网上有很多文章 比我更详细的细节,但参数嗅探的长期和短期是SQL-Server根据参数的值缓存执行计划,该值不会产生当前值的最佳执行计划。
假设Col1
有一个非聚集索引,但不包括col2
或col3
作为非关键列,那么
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;