最近參與一個對原有MS SQLSERVER2000+ASP系統升級到.Net的開發項目。其中,針對某一個數據記錄較多的表的查詢用的比較多,而且該查詢的條件組合比較復雜,包括分頁,按特定字段排序,按特定條件查詢。
參考了netkillerbaseSQL Server 存儲過程的分頁,開始決定采用效率最高的“方案二”,即通過ID標識來比較大小,從而快速檢索出所需的記錄。
為了方便讀者,我在這裡簡單列出前面列出的參考文章中的3種分頁查詢存儲過程的核心T-SQL語句:
方案一:
SELECT TOP 頁大小 *
FROM TestTable
WHERE (ID NOT IN
(SELECT TOP 頁大小*頁數 id
FROM 表
ORDER BY id))
ORDER BY ID
方案二:
SELECT TOP 頁大小 *
FROM TestTable
WHERE (ID >
(SELECT MAX(id)
FROM (SELECT TOP 頁大小*頁數 id
FROM 表
ORDER BY id) AS T))
ORDER BY ID
方案三:(利用SQL的游標存儲過程分頁)
create procedure XiaoZhengGe
@sqlstr nvarchar(4000), --查詢字符串
@currentpage int, --第N頁
@pagesize int --每頁行數
as
set nocount on
declare @P1 int, --P1是游標的id
@rowcount int
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1,@rowcount=@rowcount output
select ceiling(1.0*@rowcount/@pagesize) as 總頁數--,@rowcount as 總行數,@currentpage as 當前頁
set @currentpage=(@currentpage-1)*@pagesize+1
exec sp_cursorfetch @P1,16,@currentpage,@pagesize
exec sp_cursorclose @P1
set nocount off
上文作者使用查詢分析器比較過3種方案,結論如下:
分頁方案二:(利用ID大於多少和SELECT TOP分頁)效率最高,需要拼接SQL語句
分頁方案一:(利用Not In和SELECT TOP分頁) 效率次之,需要拼接SQL語句
分頁方案三:(利用SQL的游標存儲過程分頁) 效率最差,但是最為通用
按照“方案二”寫好查詢存儲過程後,測試中發現按照某些字段排序時,會出現記錄遺漏的情況。經過分析表中的記錄發現,原來是因為該字段內的值有重復,即用來排序的字段不能作為標識來比較大小。
找到原因後,對方案二和方案一進行了一個整合。先使用方案二中的排序字段的大小比較,來初步篩選符合條件的記錄,然後再使用方案一中的ID標識來驗證找到記錄是否合法。最後得到的T-SQL語句類似如下:
SELECT TOP 頁大小 *
FROM TestTable
WHERE (ordercol >= SELECT MAX(ordercol)
FROM (SELECT TOP 頁大小*頁數 ordercol
FROM TestTable
ORDER BY ordercol )) and (ID NOT IN
(SELECT TOP 頁大小*頁數 id
FROM TestTable
ORDER BY ordercol))
ORDER BY ordercol
這種方式繼承了“方案一”的缺點,即在記錄數相當大,而且頁碼靠後時,(SELECT TOP 頁大小*頁數 id
FROM TestTable ORDER BY ordercol)所得到的記錄集會消耗相當大的內存。但是,於此同時,前面先進行的比較判斷(ordercol >= SELECT MAX(ordercol) FROM (SELECT TOP 頁大小*頁數 ordercol FROM TestTable
ORDER BY ordercol ))所得到的記錄數量卻並不大,加上比較查詢的效率遠遠高於not in,所以最終的執行效率還是可以讓人接受的。
實際項目所使用的表中有1萬多條記錄,采用該分頁存儲過程查詢倒數前5頁,查詢執行時間低於1秒。而原來的ASP程序使用recordset的move方法,查詢執行時間超過2秒。