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

背景

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

  之前都是使用mysql和oracle,對sqlserver的使用很少。最近因項目緣由,要讀取其餘項目的數據庫,取出某個門的開關歷史記錄,而對方使用的是sqlserver,因此研究起了sqlserver的分頁,通過幾回實踐,慢慢的從低效的分頁寫到了高效的分頁。mysql

 

表結構

history表sql

歷史記錄ID:id(惟一引索)數據庫

操做時間:timeoracle

開門或關門:flagsqlserver

由誰操做:user_Id測試

屬於哪一個設備:device_id優化

 

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

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

低效的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 + 只查詢一次】是最快的一種分頁方法,並且快了好幾個檔次

相關文章
相關標籤/搜索