国产成人久久777777-国产农村妇女毛片精品久久-精品少妇人妻AV一区二区-少妇人妻精品一区二区三区-无码人妻精品一区二区

當ROWNUMBER遇到TOP -電腦資料

電腦資料 時間:2019-01-01 我要投稿
【www.gydabaoji.com - 電腦資料】

    值班期間研發同事打來電話,說應用有超時,上服務器上檢查發現有SQL大批量地執行,該SQL消耗IO資源較多,導致服務器存在IO瓶頸,細看SQL,發現自己都被整蒙了,不知道這SQL是要干啥,處理完問題趕緊研究下,

當ROWNUMBER遇到TOP

    SQL類似于:

    WITH T1 AS

    (

    SELECT TOP ( 100 )

    ID ,

    ROW_NUMBER() OVER ( ORDER BY C1 ) AS RID

    FROM  [dbo].[TB002]

    )

    SELECT *

    FROM T1

    WHERE T1.RID > (1-1)*2147483647

    AND T1.RID < 1*2147483647

    第一趕腳是寫這代碼的研發同事想分頁,但是這每頁的數據量有點嚇人啊(是我太膽小么?)

    再仔細看下,趕腳又不是分頁,上面還有TOP(100)呢?

    如果把TOP(100) 放到CTE外面,很容易理解,根據RID列過濾完后再取前100行數據。

    對于上面的TOP(100) 在CTE內部SQL執行步驟如下

    1>對表TB002中C1列排序計算每行的RID值,得到臨時結果集T1

    2>對臨時結果集T1中數據“隨機”取100條(注意:因為CTE中TOP(100) 沒有對應ORDER BY 子句,因此無法保證返回的100條數據是有序的,即使在不少場景下返回的數據是按RID排序的) 得到臨時結果集T2

    3>將臨時結果集T2的數據按照T1.RID > (1-1)*2147483647 AND T1.RID < 1*2147483647 的條件過濾,得到最終結果集T3

    4>強最終結果集T3返回給客戶端

    --=========================華麗分割線=======================================--

    在SQL SERVER 世界里,ROW_NUMBER函數已經有些泛濫成災,很多不明真相的群眾磕著瓜子就把ROW_NUMBER函數寫到應用查詢中,甚至不少研發同事(抱歉有些人躺槍了)把ROW_NUMBER函數用到登峰造極的程度,當看到一條SQL里使用到N多ROW_NUMBER函數和子查詢再加N多大表關聯查詢,我都對自己DBA的身份表示懷疑,完全看不懂啊!!!

    --=========================華麗分割線=======================================--

    回歸正題,ROW_NUMBER函數的引入是為了更簡單地實現分頁,SQL SERVER 查詢引擎會將CTE外部的條件引入到CET內部,以避免CTE內部語句執行時訪問“無用”數據,如對下面的語句

    ;WITH T1 AS

    (

    SELECT ID ,

    ROW_NUMBER() OVER ( ORDER BY ID ) AS RID

    FROM  [dbo].[TB002]

    )

    SELECT *

    FROM T1

    WHERE T1.RID > 10

    AND T1.RID < 30

    由于表TB002上ID有索引,因此查詢會利用索引訪問前30條記錄,丟棄不滿足RID>10的第1到10條數據,

電腦資料

當ROWNUMBER遇到TOP》(http://www.gydabaoji.com)。

    由于這種優化的存在,使得查詢無需先執行

    SELECT ID ,ROW_NUMBER() OVER ( ORDER BY ID ) AS RID FROM [dbo].[TB002]

    然后再執行WHERE T1.RID > 10 AND T1.RID < 30 的過濾操作。

    但如果CTE內部加入TOP子句,就使得CTE外部的T1.RID > 10 AND T1.RID < 30條件不能引入到CET內部(查詢優化器首先得保障返回結果集的正確性,然后才考慮執行的高效性)。對于研發同事也一樣,他們首先關注查詢結果是否正確,然后才考慮查詢效率是否高效,那么引入TOP是否能保證數據正確呢?

    為了掩飾,我們將查詢做輕微調整如下:

    ;WITH T1 AS

    (

    SELECT TOP(10) ID ,

    ROW_NUMBER() OVER ( ORDER BY ID ) AS RID

    FROM  [dbo].[TB002]

    )

    SELECT *

    FROM T1

    WHERE T1.RID > 10

    AND T1.RID < 30

    我們會悲哀地發現,查詢返回結果為空,這顯然不是一個好兆頭,為什么會返回空呢?

    輕輕推敲一下,我們就會發現,CTE內部的執行結果總是“巧合”地返回RID為1到10的數據,而外部條件RID>10又將這10條數據過濾掉,SO返回為空。

    PS: 查詢優化器真的是“順手”返回前10條數據,因為恰好這10條數據“在手邊”,不能保證其他場景下也是返回RID為1到10的數據,當然也不是查詢優化器故意“坑人”哈

    --=========================華麗分割線=======================================--

    至此,我總算明白為啥要將寫SQL的那位兄弟要傳入入2147483647 這么大一個頁數量,估計是傳小了查不出數據,所以一勞永逸傳個最大值,想想也是醉了!

    --=========================華麗分割線=======================================

    總結

    編寫SQL的目的在于實現業務需求,而不是顯示個人SQL能力,也沒有“一招鮮吃遍天”可以秒殺所有問題的寫法,在尊重業務需求的前提下,依據業務場景,考慮數據分布和當前以及未來的數據量,用盡可能簡單的SQL地實現業務需求才是王道。

最新文章
主站蜘蛛池模板: 襄樊市| 资阳市| 嘉禾县| 福清市| 蕉岭县| 南城县| 新田县| 夏津县| 电白县| 临海市| 缙云县| 清原| 彩票| 涿鹿县| 湾仔区| 遵义县| 莱芜市| 湾仔区| 黑河市| 泾川县| 五河县| 布拖县| 孙吴县| 晋江市| 镇安县| 崇明县| 冷水江市| 泽库县| 玉屏| 吕梁市| 彩票| 大化| 额尔古纳市| 开封市| 盐城市| 阿勒泰市| 壶关县| 合作市| 株洲县| 淮北市| 永川市|