首先感謝網友@aixuexi 在評論中的提醒,原博文介紹的幾種都不是最高效,現已修改加入另外一種更高效的方法。html
之前都是使用mysql和oracle,對sqlserver的使用很少。最近因項目緣由,要讀取其餘項目的數據庫,取出某個門的開關歷史記錄,而對方使用的是sqlserver,因此研究起了sqlserver的分頁,通過幾回實踐,慢慢的從低效的分頁寫到了高效的分頁。mysql
history表sql
歷史記錄ID:id(惟一引索)數據庫
操做時間:timeoracle
開門或關門:flagsqlserver
由誰操做:user_Id測試
屬於哪一個設備:device_id優化
下面咱們先介紹四種分頁的方法,以【一種低效】——【兩種較高效】——【一種高效】的順序進階的介紹,在最後再附上測試結果。spa
修改:通過網友@aixuexi 的提醒,加入最後一種【更高效】的方法介紹3d
思路:
最裏層:先從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就好。
最裏層:先從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...=語法速度很快,因此整體上仍是這種分頁方法更快一些。
最裏層:先從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...=要快一些。具體在下面的計較表格能夠看出。
最裏層:查詢出前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 >某個數字」 的查詢方法,又比前幾回查詢快了一點。
最裏層:查詢出前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 + 只查詢一次】是最快的一種分頁方法,並且快了好幾個檔次!