2017-05-04 50 views
0

我的SQL Server 2016上和我看到以下內容:SQL常數函數導致使用劣質查詢計劃

我有類似簡單的查詢:

select distinct col1 
from tbl 
where 
    col2 > 12345 

如果我移動不變值成一個函數,查詢計劃的變化(惡化,由A LOT):

select distinct col1 
from tbl 
where 
    col2 > dbo.fn12345() 

其中函數是

create function dbo.fn12345() 
returns int 
as begin 
    return 12345 
end 

這裏是計劃的屏幕截圖(使用我的實際模式,因此標識符與說明性示例不同。

無功能: without function

與功能: with function

隨着第二計劃執行我的時間的推移,從22秒到96S。

有沒有辦法解決這個問題,同時仍然使用函數?

請不要問我爲什麼不能內聯恆定。對於更復雜的函數也會出現同樣的問題,這些函數包括可控邏輯 - 內聯什麼是有效的複雜常數計算會改變查詢計劃。

我也知道我的索引不是最優的。這是設計。該表非常大,並且此特定查詢不保證專用索引的存儲空間。

+0

標量UDF在where子句中出現問題。有趣的是,我不能在dbfiddle上覆制。在那裏它被評估一次並用於尋找。也許這是一個已經改進的領域。什麼是你的@@版本? http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=7056d010e684b76f727bd4f38c433196 –

+0

Microsoft SQL Server 2016(SP1-CU2)(KB4013106) - 13.0.4422.0(X64) – CoderBrien

+0

由於數據和表格的原因,您可能會有一段艱難的時間。我認爲300MM行。另外我使用選項(使用提示('DISABLE_OPTIMIZED_NESTED_LOOP'))作爲加速。沒有提示它會進行排序並泄漏到tempdb。 – CoderBrien

回答

1

你總是會遇到where子句中的函數問題。甚至像ISNULL()可以改變計劃一樣簡單。 有什麼辦法可以將計算結果保存在表中(甚至是臨時表)?然後你可以交叉加入這個。 NB - 在你的表上創建統計信息,這將有助於優化器。

SELECT 12345 as val into #t 
    select distinct col1 
    from tbl 
    CROSS JOIN #t 
    where 
    col2 > val 
+0

哇,你在開玩笑嗎????!?該查詢將用作ITVF,以便臨時表不在。我會試一試......但哇。有沒有討論這個問題/解決方法的好博客? – CoderBrien

+0

omfg。 ITVF選擇一個不變的作品。真是一堆廢話!對於以下任何人,請參閱2007年以來的以下連接問題! https://connect.microsoft.com/SQLServer/Feedback/Details/273443 – CoderBrien

+0

好的。如果你的函數沒有使用任何參數,那麼你可以把它寫成一個視圖。然後SQL服務器會將視圖中的sql「內聯」到主查詢中,並且它將像常量一樣工作。 –