2

我有一个数据库,其中包含大约 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 内存,4 个真正的 4 个虚拟 CPU,Sata 磁盘。

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

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

4

1 回答 1

2

As I said in a comment, it sounds like parameter sniffing, but in the interest of being helpful I thought I'd expand on that. There are a number of articles on the web that go into a lot more detail than I will, but the long and the short of parameter sniffing is that SQL-Server has cached an execution plan based on a value for the parameter that does not yield the best execution plan for the current value.

Supposing that Col1 has a nonclustered index on, but does not include col2 or col3 as non key columns then SQL-Server has two options, it can either do a clustered index scan on My_Table to get all the rows where Col1 LIKE @letter+'%', or it can search the index on Col1 then do a bookmark lookup on the clustered index to get the values for each row returned by the index. I can't quite remember off the top of my head at what point SQL-Server switches between the two based on the estimated row count, it is at quite a low percentage, so I am fairly sure that if you are returning 150,000 records out of 2,500,000 the optimiser will go for a clustered index scan. However, if you were only returning a few hundred rows then a bookmark lookup would be preferable.

When you don't use parameters SQL-Server will create a new execution plan each time it is executed, and produce the best execution plan for that parameter (assuming your statistics are up to date), when you do use a paramter the first time they query is run sql-server creates a plan based on that particular parameter value, and stores that plan for later use. Each subsequent time the query is run sql-server recognises that the query is the same so doesn't recompile it. This means though that if the first time the query was run it was for a parameter that returned a low number of rows then the bookmark lookup plan will be stored. Then if the next time the query is run it is passed for a value that returns a high number of rows where the optimal plan is a clustered index scan then the query is still executed using the suboptimal bookmark lookup and will result in a longer execution time. This could of course also be true the other way round. There are a number of ways to get around parameter sniffing, but since your query is not very complex the compile time will not be significant, especially in comparison to the 30 seconds you say this query is taking to run even at its best, so I would use the 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);

The reason that when you tried this on a new server that it executed fine is that the first time it was run on the new server the parameterised query had to be compiled, and the plan generated was suitable to value of the parameter provided.

One final point, if you are using SQL_Server 2012 then you could use OFFSET/FETCH to do your paging:

SELECT  Col1, Col2, Col3
FROM    My_table
WHERE   Col1 LIKE @letter+'%'
ORDER BY Col1 OFFSET (@page-1) * (@pagesize) ROWS FETCH NEXT @pagesize ROWS ONLY;
于 2013-08-29T08:44:54.817 回答