[英]Do Views degrade the LINQ query performance?
這篇文章中有一些非常嚴肅的微優化。
我不會把它當成福音,與EF合作過了很多。 當然這些事情很重要,但總的來說,它很快。
如果你有一個復雜的視圖,然后你在該視圖上執行進一步的LINQ,那么肯定,它可能會導致一些緩慢的性能,但我不會賭它。
這篇文章甚至沒有任何基准分數!
如果性能是您的程序的一個嚴重問題,請縮小查詢速度並將其發布到此處,請查看SO社區是否可以幫助您優化查詢。 如果你問我,比所有微優化更好的解決方案。
這取決於,雖然很少在很大程度上。
假設我們的觀點如下:
CREATE VIEW TestView
AS
Select A.x, B.y, B.z
FROM A JOIN B on A.id = B.id
我們為此創建實體映射。
我們還假設綁定了B.id
,使其不可為空,並且與A.id
具有外鍵關系 - 也就是說,只要有B
行,總會有至少一個對應的A
現在,如果我們可以from t in context.TestView where tx == 3
執行某些操作from t in context.TestView where tx == 3
而不是from a in context.A join b in context.B on a.id equals b.id where ax == 3 select new {ax, by, bz}
。
我們可以預期前者會稍微轉換為SQL,因為它是一個稍微簡單的查詢(從Linq和SQL的角度來看)。
我們可以期望后者從SQL查詢轉換為SQLServer(或其他)內部查詢的速度稍微快一些。
我們可以預期內部查詢幾乎完全相同,除非有些奇怪。 因此,我們希望那時的表現是相同的。
總之,他們之間沒有太多選擇。 如果我不得不打賭,我會打賭使用視圖稍微快一點,特別是在第一次通話時,但我不會打賭它很多。
現在讓我們考慮一下(from t in context.TestView select tz).Distinct()
。 vs (from b in context.B select bz).Distinct()
。
這兩個都應該變成一個非常簡單的SELECT DISTINCT z FROM ...
這兩者都應該變成表B
的表掃描或索引掃描。
第一個可能不是(查詢計划中的缺陷),但這將是令人驚訝的。 (快速檢查類似的視圖確實發現SQLServer忽略了不相關的表)。
第一個可能需要稍長的時間來生成查詢計划,因為必須推斷出A.id
上的連接不相關的事實。 但是數據庫服務器在這方面做得很好; 這是一套計算機科學和問題,已經完成了數十年的工作。
如果我不得不打賭,我會打賭看起來讓事情變得非常緩慢,盡管我會更多地認為它是如此輕微的差異,它消失了。 用這兩種查詢進行的實際測試發現兩者在差異的相同范圍內(即兩者的不同時間范圍相互重疊)。
在這種情況下,從linq查詢生成SQL的效果將是nil(它們在那時實際上是相同的,但具有不同的名稱)。
讓我們考慮一下我們是否在該視圖上有一個觸發器,以便插入或刪除執行等效的插入或刪除。 在這里,我們將略微使用一個SQL查詢而不是兩個(或更多),並且更容易確保它在單個事務中發生。 因此在這種情況下,視圖略有增加。
現在,讓我們考慮一個更復雜的觀點:
CREATE VIEW Complicated
AS
Select A.x, B.x as y, C.z, COALESCE(D.f, D.g, E.h) as foo
FROM
A JOIN B on A.r = B.f + 2
JOIN C on COALESCE(A.g, B.x) = C.x
JOIN D on D.flag | C.flagMask <> 0
WHERE EXISTS (SELECT null from G where G.x + G.y = A.bar AND G.deleted = 0)
AND A.deleted = 0 AND B.deleted = 0
我們可以在linq級別完成所有這些工作。 如果我們這樣做,它可能會有點昂貴,因為查詢生成,雖然這很少是linq查詢總體命中中最昂貴的部分,雖然編譯查詢可能會平衡這一點。
我傾向於將視圖作為更有效的方法,但我會分析這是否是我使用視圖的唯一原因。
現在讓我們考慮:
CREATE VIEW AllAncestry
AS
WITH recurseAncetry (ancestorID, descendantID)
AS
(
SELECT parentID, childID
FROM Parentage
WHERE parentID IS NOT NULL
UNION ALL
SELECT ancestorID, childID
FROM recurseAncetry
INNER JOIN Parentage ON parentID = descendantID
)
SELECT DISTINCT (cast(ancestorID as bigint) * 0x100000000 + descendantID) as id, ancestorID, descendantID
FROM recurseAncetry
從概念上講,這個視圖有很多選擇; 執行選擇,然后根據選擇的結果遞歸執行選擇,依此類推,直到它具有所有可能的結果。
在實際執行中,它被轉換為兩個表掃描和一個惰性假脫機。
基於linq的等價物會更重; 你最好不要調用等效的原始SQL,或者將表加載到內存中,然后在C#中生成完整的圖形(但請注意,這將基於此不需要的查詢浪費一切)。
總之,在這里使用視圖將是一個很大的節省。
綜上所述; 使用視圖通常可以忽略不計的性能影響,並且這種影響可以是任何一種方式。 使用帶有觸發器的視圖可以獲得輕微的性能提升,並通過強制它在單個事務中發生,更容易確保數據完整性。 使用CTE視圖可以獲得巨大的成功。
使用或避免視圖的非性能原因包括:
視圖的使用會隱藏與該視圖相關的實體與代碼中與基礎表相關的實體之間的關系。 這很糟糕,因為您的模型在這方面現在還不完整。
如果視圖在除您之外的其他應用程序中使用,您將與其他應用程序更加一致,利用已經經過驗證的代碼,並自動處理視圖實現的更改。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.