优化LINQ EF。使用where而不是join?

时间:2015-12-13 17:33:34

标签: c# sql-server entity-framework linq

我写过这样的查询:

using (var _db = new LuxedecorContext())
            {
                var items = (from navItem in _db.NavItems
                             join dealItem in _db.Deal_Items
                             on navItem.ItemWebCode equals dealItem.ItemWebCode
                             join mainCategory in _db.Web_MainCategory
                             on new { A = navItem.Website, B = navItem.MainCategory }
                             equals new { A = mainCategory.Website, B = mainCategory.Code }
                             join subCategory in _db.Web_SubCategory
                             on new { A = navItem.Website, B = navItem.MainCategory, C = navItem.SubCategory }
                             equals new { A = subCategory.Website, B = subCategory.CodeMain, C = subCategory.Code }
                             select new
                             {
                                 SalePrice = navItem.SalePrice,
                                 Vendor = navItem.Vendor,
                                 Promo = navItem.Promo,
                                 Description = navItem.Description,
                                 FreightMax = navItem.FreightMax,
                                 ItemImage = navItem.ItemImage,
                                 ItemImage2 = navItem.ItemImage2,
                                 StrikedPrice = (navItem.RetailPrice ?? 0) * (1 - (navItem.PercentOff ?? 0) / 100) / (1 - navItem.PercentOffShow / 100),
                                 ItemId = navItem.ItemID,    
                                 StockQty = navItem.StockQty,
                                 SeoURL = navItem.SeoURL,                                
                                 DealSequence = dealItem.Sequence,
                                 DealId = dealItem.DealID,
                                 Website = navItem.Website
                             }).Where(x => x.SeoURL != string.Empty && x.DealId == dealId).GroupBy(x=>x.ItemId).Select(x=>x.FirstOrDefault());


                if(onlyAvalaibleItems)
                    items = items.Where(x => x.StockQty != "0");

                switch(sort)
                {
                    case "PriceAsc":
                        items = items.OrderBy(x => x.SalePrice);
                        break;
                    case "PriceDesc":
                        items = items.OrderByDescending(x => x.SalePrice);
                        break;
                    case "DescriptionAsc":
                        items = items.OrderBy(x => x.Description.Replace(x.Vendor, "").Trim());
                        break;
                    case "DescriptionDesc":
                        items = items.OrderByDescending(x => x.Description.Replace(x.Vendor, "").Trim());
                        break;
                    default:
                        items = items.OrderBy(x=>x.DealSequence);
                        break;
                }

                return await items.Select(x => new ProductCollectionItem
                {
                    SalePrice = x.SalePrice,
                    Vendor = x.Vendor,
                    Promo = x.Promo,
                    Description = x.Description,
                    FreightMax = x.FreightMax,
                    ItemImage = x.ItemImage,
                    ItemImage2 = x.ItemImage2,
                    StrikedPrice = x.StrikedPrice ?? 0
                }).ToListAsync();
            }

这是SQL,我有:

SELECT 
[Project4].[C1] AS [C1], 
[Project4].[SalePrice] AS [SalePrice], 
[Project4].[Vendor] AS [Vendor], 
[Project4].[Promo] AS [Promo], 
[Project4].[Description] AS [Description], 
[Project4].[FreightMax] AS [FreightMax], 
[Project4].[ItemImage] AS [ItemImage], 
[Project4].[ItemImage2] AS [ItemImage2], 
[Project4].[C2] AS [C2]
FROM ( SELECT 
    1 AS [C1], 
    CASE WHEN ([Limit1].[C1] IS NULL) THEN cast(0 as decimal(18)) ELSE [Limit1].[C1] END AS [C2], 
    [Limit1].[Description] AS [Description], 
    [Limit1].[SalePrice] AS [SalePrice], 
    [Limit1].[ItemImage] AS [ItemImage], 
    [Limit1].[ItemImage2] AS [ItemImage2], 
    [Limit1].[Vendor] AS [Vendor], 
    [Limit1].[Promo] AS [Promo], 
    [Limit1].[FreightMax] AS [FreightMax], 
    [Limit1].[Sequence] AS [Sequence]
    FROM   (SELECT 
        @p__linq__0 AS [p__linq__0], 
        @p__linq__1 AS [p__linq__1], 
        [Distinct1].[ItemID] AS [ItemID]
        FROM ( SELECT DISTINCT 
            [Extent1].[ItemID] AS [ItemID]
            FROM    [dbo].[NavItems] AS [Extent1]
            INNER JOIN [dbo].[Deal_Items] AS [Extent2] ON [Extent1].[ItemWebCode] = [Extent2].[ItemWebCode]
            INNER JOIN [dbo].[Web_MainCategory] AS [Extent3] ON ([Extent1].[Website] = [Extent3].[Website]) AND ([Extent1].[MainCategory] = [Extent3].[Code])
            INNER JOIN [dbo].[Web_SubCategory] AS [Extent4] ON ([Extent1].[Website] = [Extent4].[Website]) AND ([Extent1].[MainCategory] = [Extent4].[CodeMain]) AND ([Extent1].[SubCategory] = [Extent4].[Code])
            WHERE ( NOT (([Extent1].[SeoURL] = @p__linq__0) AND ((CASE WHEN ([Extent1].[SeoURL] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END) = (CASE WHEN (@p__linq__0 IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END)))) AND ([Extent2].[DealID] = @p__linq__1)
        )  AS [Distinct1] ) AS [Project2]
    OUTER APPLY  (SELECT TOP (1) 
        [Extent5].[Description] AS [Description], 
        [Extent5].[SalePrice] AS [SalePrice], 
        [Extent5].[ItemImage] AS [ItemImage], 
        [Extent5].[ItemImage2] AS [ItemImage2], 
        [Extent5].[Vendor] AS [Vendor], 
        [Extent5].[Promo] AS [Promo], 
        [Extent5].[FreightMax] AS [FreightMax], 
        [Extent6].[Sequence] AS [Sequence], 
        ((CASE WHEN ([Extent5].[RetailPrice] IS NULL) THEN cast(0 as decimal(18)) ELSE [Extent5].[RetailPrice] END) * (cast(1 as decimal(18)) - ((CASE WHEN ([Extent5].[PercentOff] IS NULL) THEN cast(0 as decimal(18)) ELSE [Extent5].[PercentOff] END) / cast(100 as decimal(18))))) / (cast(1 as decimal(18)) - ([Extent5].[PercentOffShow] / cast(100 as decimal(18)))) AS [C1]
        FROM    [dbo].[NavItems] AS [Extent5]
        INNER JOIN [dbo].[Deal_Items] AS [Extent6] ON [Extent5].[ItemWebCode] = [Extent6].[ItemWebCode]
        INNER JOIN [dbo].[Web_MainCategory] AS [Extent7] ON ([Extent5].[Website] = [Extent7].[Website]) AND ([Extent5].[MainCategory] = [Extent7].[Code])
        INNER JOIN [dbo].[Web_SubCategory] AS [Extent8] ON ([Extent5].[Website] = [Extent8].[Website]) AND ([Extent5].[MainCategory] = [Extent8].[CodeMain]) AND ([Extent5].[SubCategory] = [Extent8].[Code])
        WHERE ( NOT (([Extent5].[SeoURL] = @p__linq__0) AND ((CASE WHEN ([Extent5].[SeoURL] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END) = (CASE WHEN (@p__linq__0 IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END)))) AND ([Extent6].[DealID] = @p__linq__1) AND ([Project2].[ItemID] = [Extent5].[ItemID]) ) AS [Limit1]
)  AS [Project4]
ORDER BY [Project4].[Sequence] ASC

旧数据库中没有任何关系。我正在使用大多数连接来剪切我不需要的数据。我想知道:这是一个好方法吗?如何优化查询?

1 个答案:

答案 0 :(得分:1)

我总是不想使用连接进行过滤。有两个原因:

  • 连接可以将结果相乘(如果连接表是1:n关联),因此您必须使结果不同。这可能是表现明智的一个令人信服的理由。
  • Join 传达您希望通过联接表中的字段扩展结果集。另一方面,Any(或SQL中的EXISTS)准确地传达了您想要的内容:过滤。换句话说:使用使代码不言自明的方法。

如果你只有1..n-1关联的加入(我假设),只有第二个原因重要,因为性能不应该有太大差异。对我来说,第二个原因是令人信服的。

附注:即使数据库没有硬外键,我强烈建议您在EF模型中定义关联(导航属性)。如果你这样做,你可以使用像

这样的代码
from navItem in _db.NavItems
where navItem.DealItem != null

或者将(伪)外键作为属性拉入类模型中,这样就可以了

from navItem in _db.NavItems
where navItem.ItemWebCode.HasValue