作者 | 鄒建,資深數據庫專家,精通各項 SQL 技術,具有豐富的管理、維護、優化能力以及業務應用經驗。他一直熱心于技術知識的分享、傳播,持續活躍在 CSDN 和 MSDN 社區,曾多年蟬聯 CSDN 論壇積分榜首。
此外,鄒建還是 2004~2010、2013年度 MVP(微軟最有價值專家) 獲得者。著有《深入淺出 SQL 2005開發、管理與應用實例》《SQL 2000開發與管理應用實例》等暢銷書。
在 SQL 查詢中,不經意思的隱匿數據類型轉換可能導致極大的查詢性能問題,比如一個看起來沒有任何問題簡單的條件:WHERE c = N’x’ ,如果 c 的數據類型是 ,并且表中包含大量的數據,這個查詢可能導致極大的性能開銷sql中數據類型,因為這個操作會導致列 c 的數據類型轉換為 與常量值匹配,在 SQL 2008 及之后的版本中,這種操作做了增強,一定程度上降低了性能開銷,參考SQL 2008 處理隱式數據類型轉換在執行計劃中的增強。
不過在實際應用中發現,這種增強有時候似乎沒有起到作用,還是會存在很大的性能問題。
最近找時間做了一個測試,找出了一種可能的問題。
1. 創建一個測試表
USE TABLE _t( c (50)); INDEX IX_c ON _t( c );GO-- 加入 10000 條數據 (9999 + id) FROM( TOP 10000 id = () OVER( ORDER BY () ) FROM sys. a, sys.)ID
2. 通過執行計劃看下查詢計劃
-- 索引sql中數據類型,確保無索引碎片和統計信息準確
ALTER INDEX IX_c ON _t ;GO
SET ON
GO
* FROM _t WHERE c = N'';
GO
SET OFF;
注意列,該列值為1,表示評估的滿足條件的數據是1條,現在看起來一切正常 。
3.把數據變一下,將大量數據變成相同值
-- 將 5000 條數據值變成一樣,重建索引之后重新測試
_t SET c = '15000' WHERE c >= '15000'
ALTER INDEX IX_c ON _t ;
GO
SET ON
GO
* FROM _t WHERE c = N'10005';
GO
SET OFF;
然后我們發現評估的記錄數變大了
4. 繼續加大相同值的比例
-- 繼續加大相同值的比例,重建索引之后重新測試
_t SET c = '11000' WHERE c >= '11000' AND c < '15000'
ALTER INDEX IX_c ON _t ;
GO
SET ON
GO
* FROM _t WHERE c = N'10005';
GO
SET OFF;
GO
-- 繼續加大相同值的比例,重建索引之后重新測試
_t SET c = '10100' WHERE c >= '10100' AND c < '11000'
ALTER INDEX IX_c ON _t ;
GO
SET ON
GO
* FROM _t WHERE c = N'10005';
GO
SET OFF;
相應的,預估的行數也在增加
如果我們使用正確的數據類型,WHERE c = ‘10005’,則始終可以得到正確的預估行數。
我不確定 SQL 是按照什么標準來預估這種情況下的記錄數,從執行計劃看,它將 值通過 rt 評估出一個范圍,實際執行的是一個范圍 seek,在試驗中,查詢的值是一個常量,可以準確評估,難道這個轉換之后,把常量當變量評估了,所以是一個泛泛的評估結果值。
這個問題看起來不大,但在實際應用中,如果表的數據量很大,并且不是平均分布的話,這種錯誤的預估結果帶來的性能影響是很大的,比如明明滿足條件的很少,可以 seek, 但評估的結果很大,執行計劃變 Scan了,在復雜的執行計劃中,這個帶來的影響更大。
看起來,2008(包括R2)還沒有那么省心,這種問題還得控制,特別是程序中,.Net過來的參數通常都是 類型,這種導致性能問題的情況遇到N多了 。
最后啰嗦一下的是,在 SQL 2014中,沒有再發現這個問題(不知道 2012中怎么樣)
原創:鄒建。
投稿:有投稿意向技術人請在公眾號對話框留言。