2016-12-15 44 views
2

因此... EF6正在創建一個可怕的低效查詢。我有一個查詢針對具有三種不同類型地址的數據源。我有一個地址標識列表,這些地址標識可能是用戶試圖使用的新標識的重複項。理想情況下,我希望此查詢檢查是否有多個地址標識符都位於給定ID集合中。目前此查詢:如何讓EF6在(...)查詢中生成高效

return await _tickets.Where(t => 
    t.Metadata is SIFTEscalationMetadata && (
     addesses.Any(a => a == (t.Metadata as SIFTEscalationMetadata).Address.Id) || 
     addesses.Any(a => a == (t.Metadata as SIFTEscalationMetadata).AddressEntered.Id) || 
     addesses.Any(a => a == (t.Metadata as SIFTEscalationMetadata).CleanedAddress.Id))).ToArrayAsync(); 

正在成爲這樣的:

SELECT 
    [Project1].[TicketId] AS [TicketId], 
    [Project1].[TicketType] AS [TicketType], 
    [Project1].[Opened] AS [Opened], 
    [Project1].[Closed] AS [Closed], 
    [Project1].[Modified] AS [Modified], 
    [Project1].[EscalationStatusText] AS [EscalationStatusText], 
    [Project1].[QualificationStatusText] AS [QualificationStatusText], 
    [Project1].[ProductsText] AS [ProductsText], 
    [Project1].[Cancelled] AS [Cancelled], 
    [Project1].[CancellationReason_Id] AS [CancellationReason_Id], 
    [Project1].[CreatedBy_Id] AS [CreatedBy_Id], 
    [Project1].[Metadata_Id] AS [Metadata_Id], 
    [Project1].[NotesContainer_Id] AS [NotesContainer_Id] 
    FROM (SELECT 
     [Extent1].[TicketId] AS [TicketId], 
     [Extent1].[TicketType] AS [TicketType], 
     [Extent1].[Opened] AS [Opened], 
     [Extent1].[Closed] AS [Closed], 
     [Extent1].[Modified] AS [Modified], 
     [Extent1].[EscalationStatusText] AS [EscalationStatusText], 
     [Extent1].[QualificationStatusText] AS [QualificationStatusText], 
     [Extent1].[ProductsText] AS [ProductsText], 
     [Extent1].[Cancelled] AS [Cancelled], 
     [Extent1].[CancellationReason_Id] AS [CancellationReason_Id], 
     [Extent1].[CreatedBy_Id] AS [CreatedBy_Id], 
     [Extent1].[Metadata_Id] AS [Metadata_Id], 
     [Extent1].[NotesContainer_Id] AS [NotesContainer_Id], 
     CASE WHEN ([Extent2].[TicketMetadataID] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '2X0X' END AS [C1] 
     FROM [dbo].[Tickets] AS [Extent1] 
     LEFT OUTER JOIN [dbo].[TicketMetadata] AS [Extent2] ON ([Extent2].[Discriminator] = N'SIFTEscalationMetadata') AND ([Extent1].[Metadata_Id] = [Extent2].[TicketMetadataID]) 
    ) AS [Project1] 
    WHERE ([Project1].[C1] LIKE '2X0X%') AND ((EXISTS (SELECT 
     1 AS [C1] 
     FROM (SELECT 
      486524 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable1] 
     UNION ALL 
      SELECT 
      486525 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable2] 
     UNION ALL 
      SELECT 
      486526 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable3] 
     UNION ALL 
      SELECT 
      508376 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable4] 
     UNION ALL 
      SELECT 
      508377 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable5] 
     UNION ALL 
      SELECT 
      508378 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable6]) AS [UnionAll5] 
     LEFT OUTER JOIN (SELECT 
      [Extent3].[Address_Id] AS [Address_Id], 
      '2X0X' AS [C1] 
      FROM [dbo].[TicketMetadata] AS [Extent3] 
      WHERE ([Extent3].[Discriminator] = N'SIFTEscalationMetadata') AND ([Project1].[Metadata_Id] = [Extent3].[TicketMetadataID])) AS [Project8] ON 1 = 1 
     WHERE [UnionAll5].[C1] = (CASE WHEN ([Project8].[C1] LIKE '2X0X%') THEN [Project8].[Address_Id] END) 
    )) OR (EXISTS (SELECT 
     1 AS [C1] 
     FROM (SELECT 
      486524 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable7] 
     UNION ALL 
      SELECT 
      486525 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable8] 
     UNION ALL 
      SELECT 
      486526 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable9] 
     UNION ALL 
      SELECT 
      508376 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable10] 
     UNION ALL 
      SELECT 
      508377 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable11] 
     UNION ALL 
      SELECT 
      508378 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable12]) AS [UnionAll10] 
     LEFT OUTER JOIN (SELECT 
      [Extent4].[AddressEntered_Id] AS [AddressEntered_Id], 
      '2X0X' AS [C1] 
      FROM [dbo].[TicketMetadata] AS [Extent4] 
      WHERE ([Extent4].[Discriminator] = N'SIFTEscalationMetadata') AND ([Project1].[Metadata_Id] = [Extent4].[TicketMetadataID])) AS [Project16] ON 1 = 1 
     WHERE [UnionAll10].[C1] = (CASE WHEN ([Project16].[C1] LIKE '2X0X%') THEN [Project16].[AddressEntered_Id] END) 
    )) OR (EXISTS (SELECT 
     1 AS [C1] 
     FROM (SELECT 
      486524 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable13] 
     UNION ALL 
      SELECT 
      486525 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable14] 
     UNION ALL 
      SELECT 
      486526 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable15] 
     UNION ALL 
      SELECT 
      508376 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable16] 
     UNION ALL 
      SELECT 
      508377 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable17] 
     UNION ALL 
      SELECT 
      508378 AS [C1] 
      FROM (SELECT 1 AS X) AS [SingleRowTable18]) AS [UnionAll15] 
     LEFT OUTER JOIN (SELECT 
      [Extent5].[CleanedAddress_Id] AS [CleanedAddress_Id], 
      '2X0X' AS [C1] 
      FROM [dbo].[TicketMetadata] AS [Extent5] 
      WHERE ([Extent5].[Discriminator] = N'SIFTEscalationMetadata') AND ([Project1].[Metadata_Id] = [Extent5].[TicketMetadataID])) AS [Project24] ON 1 = 1 
     WHERE [UnionAll15].[C1] = (CASE WHEN ([Project24].[C1] LIKE '2X0X%') THEN [Project24].[CleanedAddress_Id] END) 
    ))) 

什麼是讓EF在這裏產生更好的查詢的最佳方式?這將是很好,如果它可能只是做:

SELECT ... 
WHERE Address_Id in(486524, 486525, 486526, 508376, 508377, 508378) 
     OR AddressEntered_Id in(486524, 486525, 486526, 508376, 508377, 508378) 
     OR CleanedAddress_Id in(486524, 486525, 486526, 508376, 508377, 508378) 
+3

'包含()'產生更好的SQL嗎?包含((t.Metadata as SIFTEscalationMetadata).Address.Id)|| ......對於小數據集,我已經看到EF5在使用Contains時會生成一個簡短的IN()。 –

+0

謝謝,就是這樣。 – Jereme

回答

4

由於@Cory指出Contains擴展方法是在SQL轉換爲IN,所以你應該用它代替Any這是翻譯成EXIST

return await _tickets.OfType<SIFTEscalationMetadata>() 
        .Where(t =>addesses.Contains(t.Address.Id) || 
           addesses.Contains(t.AddressEntered.Id) || 
           addesses.Contains(t.CleanedAddress.Id)).ToArrayAsync(); 

,你也應該使用OfType擴展方法得到的只有SIFTEscalationMetadata實體

+0

唉,似乎_ticket不是SIFTEscaltionMetaData,但_ticket有一個屬性MetaData,其中一些是SIFTEscalatnionMetaData,可能有些不是。如果t是正確的類型,OfType不檢查屬性t.MetaData是否具有正確的類型,總線。 –

+0

是的,我結束的實際代碼有點不同,但使用Contains確實有效。我認爲EF在檢查.Any(...)的整數數組時選擇使用exists是很奇怪的,但它是... – Jereme

0

從你的代碼示例我認爲以下幾點。

_tickets是一個類的項目序列,比如說一個Ticket。並且序列中的每個項目都有一個屬性MetaData。 MetaData的返回值可能是也可能不是SIFTEslationMetaData。 (這是真的嗎?還是每個MetaData都是SIFTEscalationMetaData?)

當屬性MetaData的值是SIFTEslationMetaData時,您確定屬性Ticket.MetaData至少有三個其他非空屬性:Address,AddressEntered和CleanedAddress 。此外,您可以確定這三個屬性不會返回NULL,並且它們都有一個屬性Id。

此外,你也有一個序列addesses,其中每個元素是屬性Id相同的類型。我不知道Id的類型,但我們假設它是IdType,可能是int或字符串等

顯然你只想從你的_tickets序列中屬性MetaData是SIFTEslationMetaData的那些票據,並且Ticket.MetaData.Address,Ticket.MetaData.AddressEntered或Ticke.MetaData.CleanedAddress的至少一個Id值位於addesses集合中。

IEnumerable<Ticket> result = _tickets 
    // first remember the ticket and convert the MetaData 
    // to either null or a SIFTEscalationMetadata 
    .Select(t => new 
    { 
     Ticket = t, 
     MetaData = t.Metadata as SIFTEscalationMetadata, 
    }) 
    // now take only those tickets where the metadata is not null 
    .Where(t => t.MetaData != null) 
    // and select from the remaining tickets the Ids you want to check 
    .Select(t => new 
    { 
     Ticket = t.Ticket, 
     Ids = new IdType[] 
     { 
      t.MetaData.Address.Id, 
      t.MetaDate.AddressEntered.Id, 
      t.MetaData.CleanedAddress.Id, 
     }, 
    }) 
    // now take only those items where the intersection of the Ids and 
    // addesses has any elements 
    .Where(t => addess.Interset(t.Ids).Any()) 
    .Select(t => t.Ticket); 

優化是在中間選擇爲匿名類型。匿名類型將成爲SQL變量。每張票只能轉換爲SIFTEslationMetaData一次。檢查是否有任何Ids是在addesses中僅進行一次,並且只對具有正確MetaData的票據進行。

+0

我將不得不通過分析器運行它,看看它吐出了什麼。可能很有趣。使用如上所述的包含已經將查詢時間從不可接受的降低到可接受的(2秒內降到幾分之一秒)。 – Jereme

+0

所以我只是測試了這一點;使用Contains並將各種鑄件留在一個合理的位置上。兩種方法的結果都是相同的讀取次數,但包含in(...)的結果會在360ms內生成結束,而此方法需要大約530ms。 – Jereme