2

我有一個基本上是帶有標籤字典的文檔的數據結構。我試圖將所有帶有名爲'Last Name'的標籤和標籤值爲'Smith'的表單類型的文檔帶回。可能有0..N'姓'標籤與文檔關聯。優化實體框架生成的SQL Server執行計劃

我使用下面的LINQ查詢,試圖用匹配標籤匹配源文件兒童:

DB.Documents 
    .Where(doc => doc.FormID == pd.IndexForm.FormID) 
    .Where(doc => doc.Document_StringIndex_ReadOnly 
       .Join(Fields, 
         dsi => new { FieldName = dsi.FieldName, FieldValue = dsi.StringValue }, 
         dsi2 => new { FieldName = dsi2.FieldName, FieldValue = dsi2.StringValue }, 
         (dsi, dsi2) => dsi.Document).Count() > 0); 

產生以下查詢時使用.ToTraceString()輸出

SELECT 
[Project1].* 
FROM (SELECT 
    [Extent1].* 
    (SELECT 
     COUNT(cast(1 as bit)) AS [A1] 
     FROM [dbo].[Document_StringIndex_ReadOnly] AS [Extent2] 
     INNER JOIN (SELECT [Extent3].* 
      FROM [dbo].[Document] AS [Extent3] 
      INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID]) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL))) 
     LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID] 
     WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = @p__linq__7) AND ([Join1].[FieldName] = @p__linq__8)) AS [C1] 
    FROM [dbo].[Document] AS [Extent1] 
    WHERE [Extent1].[FormID] = @p__linq__5 
) AS [Project1] 
WHERE [Project1].[C1] > 0 

如果我將常量直接替換爲我的參數(如下所示),則查詢執行得非常快。但是,如果我保留參數,查詢需要幾分鐘的時間。

SELECT 
[Project1].* 
FROM (SELECT 
    [Extent1].* 
    (SELECT 
     COUNT(cast(1 as bit)) AS [A1] 
     FROM [dbo].[Document_StringIndex_ReadOnly] AS [Extent2] 
     INNER JOIN (SELECT [Extent3].* 
      FROM [dbo].[Document] AS [Extent3] 
      INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID]) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL))) 
     LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID] 
     WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = 1015) AND ([Join1].[FieldName] = 'DDKey')) AS [C1] 
    FROM [dbo].[Document] AS [Extent1] 
    WHERE [Extent1].[FormID] = 22 
) AS [Project1] 
WHERE [Project1].[C1] > 0 

生成執行計劃後,我瞭解到如果直接替換參數值,SQL Server會執行索引查找,並且查詢速度很快。只要我保留參數,SQL Server將執行索引掃描,並且我的查詢超時。有沒有什麼辦法可以讓SQL服務器始終查找?我可以強制實體框架不使用參數化查詢嗎?

+0

您使用的是哪種版本的Linq to Entities? – Phil

回答

4

在生成的SQL,這條線

[Join1].[FieldName] = @p__linq__8 

可能是問題。

如果FieldNamevarchar(...)@p__linq__8nvarchar(...)那麼這個子句都將導致表掃描因爲參數類型不匹配的索引類型。

當您直接替換'DDKey'時,那麼類型匹配,所以你得到一個索引查找。嘗試使用N'DDkey查詢',看看你是否得到了表掃描。

這是各種版本的Linq to Sql和Linq to Entities的問題,但可能在以後的版本中得到修復。

如果無法更新到最新版本,解決此問題的一種方法是將FieldName更改爲nvarchar(...)

+0

謝謝。將我的參數更改爲varchar將生成正確的執行計劃。用nvarchar列類型重建表和索引需要一段時間,但看起來像是我的問題。 – user1431334