SQL優化案例 RowNumber分頁

2022-02-15 03:03:45 字數 1423 閱讀 2753

將業務語句翻譯成sql語句不僅是一門技術,還是一門藝術。

下面拿我們程式開發工程師最常用的row_number()分頁作為乙個典型案例來說明。

先來看看我們最常見的分頁的樣子:

的確,這樣的寫法很符合我們的思維邏輯,並且我們在recid上建立非聚集索引,那麼它的效率看上去也是不錯的。當然根據這條sql,最佳索引實踐應該是:

但是,這真的是最佳的了嗎?當sql的where條件變多,table_a.其它字段變得越來越多,over()子句中的orderby欄位越來越多或者變成order by columna/columnb這樣的計算表示式,這條語句變得越來越不堪重負,最終效能問題凸現出來,另外,作為dba,我們總是盡量維持索引的簡單性、可重用度,而不想建立成為某個語句專用的索引。舉例來說,在include中,我們總不能把table_a.其它欄位中的所有欄位都放進去吧,個數少還行,如果遇上幾十個字段或者有大容量字元字段,維護成本將大大增加,那將是我們不願意看到的。

這個時候就要求我們看看是否能對語句做出一些優化了。

在上面的sql中,我們看它的執行計畫,我已經建立了索引,該索引並未include select列表中的其它字段:

根據上圖的執行計畫,可以看到,where條件走的是我剛剛建立的索引,下面的鍵查詢與其並行,我們先不討論該執行計畫的具體細節,下面我們來設想幾個問題:

在where條件簡單,並且索引合適,統計資訊正確的前提下,sql server可以很容易獲得那50行,並且回到聚集索引中找到屬於它的其它欄位的資料,這是sql server的智慧型編譯的結果,也是我們希望看到的返回方式。

但是,在where條件較為複雜,多個where條件均為範圍字段或者狀態字段時,執行計畫也許並沒有我們想象的那麼智慧型了,比如它可能採用這樣的方式:

當sql server無法準確的取出你要的那些行時,那麼它便會取回全部的行數後,再去聚集索引中找回屬於它的其它欄位的資料,當where條件可以返回幾十萬資料時,你可以想象它的效率有多低,它會仍然使用上文中類似的執行計畫,這顯然不是我們希望看到的。

我們想看到的是什麼?

1、根據where條件和排序規則,先取出那50條資料所屬的主鍵。

into #1

from table_a with ( nolock )

where recid = 220051

2、利用上個步驟中返回的主鍵,去原始表取回這50條記錄的其它字段資料。

那麼,上面兩個步驟合在一起:

很好,現在我們再來看一下這個sql的執行計畫:

binggo!這才是我們理想中的樣子!

針對這個sql,我們只需要建立乙個合適的索引,而不用顧忌select列表中那些煩人的其它列,因為他們回聚集索引取資料,也不過幾百個io而已(需要返回的行數*index_level)。它不需要再為過期的統計資訊或者錯誤的執行計畫而付出沉重的代價!

總結:sql優化,是一門藝術。

SQL使用Apply實現row number效果

sql server 2005 新增cross可以參見文章 需求 不同班級的同學,成績對應不同等級,且不同等級的數量還不同。如1班分 90分優秀,80分良好,70中等,60分及格四擋,2班分,85分良好,60及格兩檔。有表如下,簡易的表,如果需要輔助列可以隨便加的。人員表eid score did ...

SQL優化案例150811

最近遇到個sql 跑的很慢,第一次12秒出結果 從磁碟讀入記憶體 第二次2秒出結果 資料都在buffer cache裡面 sql 如下 html view plain copy print?select count 0 from p left join r on p.id r id p 有200w ...

sql中用CTE實現增加row number 列

增加一列 alter table table1 add id int default 0 用cte更新建立的這個id列 with cte as select rn row number over order by getdate from table1 update cte set id rn 補充...