SQL調優學習之——sqlserver分頁從低效到高效


背景

  首先感謝網友@aixuexi 在評論中的提醒,原博文介紹的幾種都不是最高效,現已修改加入另一種更高效的方法。

  以前都是使用mysql和oracle,對sqlserver的使用不多。最近因項目原因,要讀取其他項目的數據庫,取出某個門的開關歷史記錄,而對方使用的是sqlserver,所以研究起了sqlserver的分頁,經過幾次實踐,慢慢的從低效的分頁寫到了高效的分頁。

 

表結構

history表

歷史記錄ID:id(唯一引索)

操作時間:time

開門或關門:flag

由誰操作:user_Id

屬於哪個設備:device_id

 

下面我們先介紹四種分頁的方法,以【一種低效】——【兩種較高效】——【一種高效】的順序進階的介紹,在最后再附上測試結果。

修改:經過網友@aixuexi 的提醒,加入最后一種【更高效】的方法介紹

低效的sql

 思路:

 

最里層:先從history表根據時間倒序查出前50010條記錄

中間層:從以上的查詢結果中根據時間正序查出前10條記錄,一正一反剛好就拿出了第10000條到10010條記錄了。

最外層:根據時間倒序拿出以上的查詢結果

SQL代碼:

select * from 
(
    select top 10 * from 
    (
        select top 50010 * from history 
        order by 
        time desc  
    ) h 
    order by  
    h.time asc
) hh  
order by  
hh.time desc

 

經下面的檢驗,這種查詢效率比較低下。

其原因是因為每一層的查詢都使用了select * ,即掃描所有的這段,但是 “最里層” 和 “中間層” 根本就沒必要select * ,這兩層目的只是為了把最后一層的搜索范圍定位在第10000-10010條之間,所以,在這兩層里,我們只要拿出關鍵字ID和排序字段time就好。

較高效的SQL(1)————使用where ... =

 

最里層:先從history表根據時間倒序查出前50010條記錄,只拿出id和time

中間層:從以上的查詢結果中根據時間正序查出前10條記錄,只拿出id和time,一正一反剛好就拿出了第10000條到10010條記錄了。

最外層:根據時間倒序拿出以上的查詢結果,select *拿出所有字段,查詢范圍用where ... = ... 來匹配。

SQL代碼:

select * from history hh,
( 
    select top 10 id ,time from  
    ( 
        select top 50010 id ,time from history  
        order by 
        time desc  
    ) h  
    order by  
    h.time asc 
) hhh  
where hhh.id = hh.id 
order by  
hhh.time desc

 

經檢驗,這種分頁 方法比上一種快一點點。

主要原因在與“最里層”和“中間層”的兩次查詢,都只是查出id和time,而不是select * ,從這個角度講提升了效率。但最后又用了where...=語法,比起上一種分頁方法,又降低了一點效率。但是where...=語法速度很快,所以總體上還是這種分頁方法更快一些。

較高效的分頁(2)————使用where...in

 

最里層:先從history表根據時間倒序查出前50010條記錄,只拿出id和time

中間層:從以上的查詢結果中根據時間正序查出前10條記錄,只拿出id和time一正一反剛好就拿出了第10000條到10010條記錄了。

最外層:根據時間倒序拿出以上的查詢結果,select *拿出所有字段,查詢范圍用where...in ()來匹配

SQL代碼:

select * from history hh 
where id in 
( 
    select top 10 id from  
    ( 
        select top 50010 id ,time from history  
        order by  
        time desc 
    ) h  
    order by 
    h.time asc 
)
order by  
hh.time des

這種分頁方法,與上一種分頁方法比起來,區別是這種使用了where...in(),而不是where...=,原理上差別不大,但可能是SQLServer內部優化的原因,使用where...in比使用where...=要快一些。具體在下面的計較表格可以看出。

高效的分頁————使用row_number() over

 

最里層:查詢出前50010條數據,只拿出ID字段,同時使用row_number() over 語法,增加一個n字段,代表該條數據時第幾行

最外層:根據where...來匹配id,同時直接拿出 n>50000 的數據。

SQL代碼:

select hhh.n , hh.* from history hh ,
( 
    select top 50010 row_number() over  
    ( 
        order by  
        time desc 
    ) n,id 
    from history  
) hhh 
where hhh.id = hh.id 
and hhh.n > 50000 
order by  
hhh.n desc

這種分頁方法,首先只是兩次查詢,這無非提高了效率。最里層查詢出來的“虛列”——n,sqlserver不知道會不會為其加上索引,個人認為會,但想不出什么驗證的方法。假如有加入索引的話,那在這個地方,使用 “n >某個數字” 的查詢方法,又比前幾次查詢快了一點。

更高效的分頁——row_number() over + 只查詢一次

 

最里層:查詢出前50010條數據,使用row_number() over 語法,增加一個n字段,代表該條數據時第幾行,同時查出我們想要的信息,這里跟其他方法一樣查詢出所有:*

最外層:根據where...來匹配n,同時直接拿出 n>50000 且 n<50010 行的數據。

SQL代碼:

select * from 
(
    select *,row_number() over  
    (
        order by  
        time desc
    ) n 
    from history  
) hhh 
where hhh.n > 50000 
and hhh.n <= 50010 

 

幾種分頁方法速度比較

 以下進行兩種測試,第一種是查詢出1000-1010條數據,第二種是查詢出第50000-50010條數據。記錄的秒數是查詢50次總共的用時,每組測試5次,最后取平均值。

 

【查詢1000-1010條數據】 第一次 第二次 第三次 第四次 第五次 平均
低效的分頁 6.344s 5.687s 5.797s 5.704s 5.641s 5.835s
較高效的分頁(1)——where...= 4.485s 5.281s 5.094s 5.281s 5.313s 5.091s(勝出)
較高效的分頁(1)——where...in 5.093s 5.328s 5.14s 5.406s 5.297s 5.253s
高效的分頁——row_number() over 5.437s 5.39s 5.156s 5.016s 5.344s 5.269
更高效的分頁——row_number() over + 只查詢一次 5.188s 4.875s 5.172s 4.953s 4.875s 5.0126s(勝出)

從中可以看出,在查詢的行數較少時,使用 【更高效的分頁——row_number() over + 只查詢一次】是最快的一種分頁方法。

 

【查詢50000-50010條數據】 第一次 第二次 第三次 第四次 第五次 平均
低效的分頁 10.844s 9.985s 10.172s 10.0s 10.297s 10.260s
較高效的分頁(1)——where...= 9.625s 9.469s 9.14s 9.171s 9.219s 9.325s
較高效的分頁(1)——where...in 9.156s 9.61s 9.187s 9.218s 9.219s 9.278s
高效的分頁——row_number() over 7.844s 6.765s 6.422s 7.359s 6.875s   7.05s(勝出)
更高效的分頁——row_number() over + 只查詢一次 6.730s 6.109s 7.109s 5.328s 5.515s 6.086s(勝出)

從中可用看出,在查詢的行數較多時,使用【更高效的分頁——row_number() over + 只查詢一次】是最快的一種分頁方法,而且快了好幾個檔次

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM