3

我正在使用SQL Server 2008R2。我已經編寫了下面的表值UDF,它接受標量值this或者this或者兩者作爲參數,並返回一個帶有列ID的表,this和那個表。當我從一個複雜的查詢中調用這個函數時,我看到了糟糕的表現,但當我在簡單的查詢中調用它時,卻看不到這種性能。我想知道是否有人對我正在做的事情有任何想法,這會減慢速度。函數的定義如下:內聯表值UDF性能

CREATE function dbo.fn_getThisThat (@this nvarchar(255), @that nvarchar(255)) 
RETURNS TABLE 
RETURN 

SELECT These.this, Those.that, COALESCE(These.ID, Those.ID) as ID 
FROM 
    (
    SELECT col1 as ‘this’, value1, value2, ID 
    FROM (
     SELECT t1.col1, t1.col2, t1.col3, t2.col1 
     FROM t1 
     JOIN t2 
      ON t1.col1 = t2.col1 
     WHERE t2.col2 = ‘this’ 
     AND t1.col1 in (‘value1’, ‘value2’) 
     ) SOURCE 
    PIVOT (
     MAX(t1.col3) FOR t1.col1 in (value1, value2) 
     ) AS pvt 
    ) These 
JOIN 
    (
    SELECT t1.col1, t1.col2, t2.col1, t3.ID 
    FROM t3 
    JOIN t1 
     ON t3.col1 = t1.col1 
    JOIN t2 
     ON t2.col1 = t1.col1 
    WHERE t3.col3 = ‘value3’ 
    AND t1.col3 = ‘value1’ 
    AND t2.col3 = ‘value2’ 
    ) Those 
WHERE that = @that 
OR this = @this 

下面的語句過程非常迅速(< 1秒)時傳遞標量參數:

SELECT * FROM dbo.fn_getThisThat(scalarValue, null) 

或者在像一個相對簡單的查詢:

SELECT t1.col1, t1.col2, fn.This 
FROM t1 
CROSS APPLY dbo.fn_getThisThat(t1.col3, null) 

...但是在像這樣更復雜的查詢中調用時(在僞代碼中:讓我知道它是否沒有足夠的信息),它滯後HORRIBLY(從處理時間約1秒到約2:30秒):

DECLARE @table (a, b, c) 
INSERT @table (a, b, c) 
SELECT (values) 

SELECT t1.c1, t1.c2, t1.c3 
FROM 
    (
    SELECT a.c1, COUNT(fn.That) as c2, COUNT(a.c2) as c3 
    FROM a 
    JOIN b ON (join terms) 
    CROSS APPLY dbo.fn_getThisThat(a.c2, null) fn 
    WHERE a.c1 IN (SELECT a FROM @table) 
    GROUP BY a.c1 
    ) t1 

有沒有人有什麼建議我在做什麼來殺死在這第二個查詢的速度?我改變了函數接受一個數組而不是標量參數,但是這消除了我的跨應用能力(在最後的代碼片段中)。就我從查詢分析器中可以看出的性能影響來自於我的函數的交叉應用。我以爲我不會遇到RBAR,因爲我的UDF不是多語言,但也許我是錯的......?

編輯: 還有一件事:查詢執行計劃顯示該函數本身只貢獻2%的批處理;較大的查詢貢獻了98%,但其大部分成本來自索引搜索和表掃描,而不是來自並行性。這使我認爲函數調用可能不是指責查詢緩慢的原因,而是某些表所涉及的索引缺乏(不幸的是,我並沒有很多控制權來增加指數)。我在沒有調用函數的情況下運行查詢,並且表掃描和索引查找仍顯示爲高,但查詢在大約8秒內完成。那麼,我們回到了功能......?

回答

0

我認爲你最好的選擇是在SSMS中運行它並檢查你的執行計劃。由於這是一個內聯的表值UDF,因此優化器將把它併入到執行計劃中,並且應該能夠看到事物正在脫軌。

我沒有太多的經驗在CROSS APPLY情況下使用PIVOT子查詢 - 這讓我覺得這可能是一個問題。但執行計劃肯定會告訴你。

1

從MSDN文章應用(MSDN - Apply):

「APPLY運算符可以讓您調用表值函數對於查詢的外部表表達式返回的每一行」。

你的例子顯示了一個group by。在行被分組之後而不是在特定的查詢中調用你的函數是否可能?這將減少函數必須被調用的行數。

如果做不到這一點,我的另一個建議是通過優化查詢,在函數本身中獲得儘可能多的性能增益。你可以加快每一毫秒的速度。

+0

感謝您的建議:我不確定我可以在應用函數之前進行分組,因爲它作爲一個「翻譯器」將其轉換爲該函數,反之亦然。這個想法是計算這個數字並按照ID進行分組:只要我能夠確定我是否先嚐試將整數轉換爲這個或那個,這是行不通的。 – AnnStimmel

1

正如已經指出的那樣,爲外部查詢中的每一行調用CROSS APPLY。所以,這裏的關鍵問題是返回多少行:

DECLARE @table (a, b, c) 
INSERT @table (a, b, c) 
SELECT (values) 

SELECT t1.c1, t1.c2, t1.c3 
FROM 
    (
    SELECT a.c1 
    FROM a 
    JOIN b ON (join terms) 
    WHERE a.c1 IN (SELECT a FROM @table) 
    ) t1 

這是將要對您的TVF進行的調用次數。如果(並且它很大)TVF對於a.c2的任何值具有類似的執行時間,那麼相關的性能比較就是你的函數的單獨執行時間*從上面的查詢返回的行。

由於原始查詢的混淆/泛化,難以確定,但我懷疑您的TVF可能被排除,並且邏輯內聯到父查詢中。如果可行,這可能會讓你獲得最佳表現。

+0

謝謝,馬克!你對我的概括是正確的:我擔心我把它搞得太過分了。上面引用的僞代碼選擇語句是4中的一個,每個語句都調用該函數。如果我要消除這個函數並且使用邏輯內聯,我將不得不在這個查詢中執行4次(在整個系統中還有更多)。我看到,我正在調用交叉應用的每一行的函數,但是由於它是單一語句,因此認爲它相對便宜。 – AnnStimmel

0

到目前爲止,我已經能夠將性能從〜2:30提高到〜0:17。這更好,但仍不理想。我做了以下內容:

  • 新增架構綁定到我的電視UDF(謝謝你,萊姆斯!)。這有所幫助,但似乎對性能的影響比以下更少。

  • 重新構建的主查詢在@table上加入,而不是在子查詢中引用它:這看起來最有幫助,並且是大部分性能提升似乎來自的地方。

我覺得我剩下的滯後是由於一些大表我打嚴重缺失指數,但沒有增加他們的能力,我不知道我能做到的ATM。根據查詢分析器的報告,我已經將並行性的成本降低到了0%,所以我認爲我已經盡全力去完成函數調用。

謝謝大家!

+1

在QA/SSMS中執行計劃的百分比是謹慎的,而不是實際成本。特別是幻想會嚴重影響計劃中的估計與實際成本。此外,作爲您的新手......任何您認爲有用的答案應該是「upvoted」,任何您認爲解決問題的答案都應該標記爲這樣。 –