简体   繁体   English

EF核心QueryFilter的性能问题

[英]Performance problems with EF core QueryFilter

In our system we ran into performance problems while using QueryFilters in EF core. 在我们的系统中,我们在EF核心中使用QueryFilters时遇到了性能问题。 The problem is that EF core filters inside a LEFT JOIN instead of doing the filtering outside of it. 问题是EF核心在LEFT JOIN内部过滤而不是在其外部进行过滤。

The generated SQL looks something like this: 生成的SQL看起来像这样:

SELECT [pom].[Id],
        [pom].[DeleteDate],
        [pom].[UpdateDate],
        [pom].[Version],
        [t].[Id],
        [t].[MandatorId],
        [t].[NetPriceAmount],
        [t].[NetPriceCurrencyIso4217Code]
FROM [externaldata].[PurchaseOfferMetadata] AS [pom]
    LEFT JOIN (
    SELECT [po].[Id],
            [po].[MandatorId],
            [po].[NetPriceAmount],
            [po].[NetPriceCurrencyIso4217Code]
    FROM [externaldata].[PurchaseOffer] AS [po]
    WHERE [po].[MandatorId] = 1
) AS [t] ON [pom].[Id] = [t].[Id]
WHERE [pom].[Id] IN 
    (CAST(3094411 AS bigint), 
    CAST(4757070 AS bigint), 
    CAST(4757112 AS bigint), 
    CAST(5571232 AS bigint))

The problematic part is WHERE [po].[MandatorId] = 1 . 有问题的部分是WHERE [po].[MandatorId] = 1 If this would be in the second WHERE statement, the query runs much faster. 如果这将在第二个WHERE语句中,则查询运行得更快。

The database model is configured like this: 数据库模型配置如下:

modelBuilder.Entity<PurchaseOffer>()
      .HasQueryFilter(po => po.MandatorId == 1)
      .ToTable(nameof(PurchaseOffer), schema: ExternalDataSchemaName);

modelBuilder.Entity<PurchaseOfferMetadata>()
      .HasOne(pom => pom.PurchaseOffer)
      .WithOne(po => po.Metadata)
      .HasForeignKey<PurchaseOffer>(po => po.Id);

On the database we have set the foreign key like this: 在数据库上我们设置了这样的外键:

IF OBJECT_ID('[externaldata].[FK_PurchaseOffer_PurchaseOfferMetadata]', 'F') IS NULL
BEGIN
    ALTER TABLE [externaldata].[PurchaseOffer] ADD CONSTRAINT [FK_PurchaseOffer_PurchaseOfferMetadata] FOREIGN KEY
    (
        [Id]
    )
    REFERENCES [externaldata].[PurchaseOfferMetadata] ([Id])
END;

The EF core query looks like this: EF核心查询如下所示:

var existingPurchaseOfferMetadatasById = await db.PurchaseOfferMetadatas
   .Where(pom => purchaseOfferIds.Contains(pom.Id))
   .Include(pom => pom.PurchaseOffer)
   .ToDictionaryAsync(pom => pom.Id, cancellationToken);

Currently we have the following amount of records in each table: 目前,我们在每个表中都有以下数量的记录:

  • PurchaseOfferMetadata: 12'654'639 PurchaseOfferMetadata:12'654'639
  • PurchaseOffer: 1'689'634 PurchaseOffer:1'689'634

Has anyone also encountered this problem and might found a solution to this? 有没有人也遇到过这个问题,可能会找到解决方案吗?

EF has historically not done a great job of creating the most optimized SQL queries when joins are complex. 在连接很复杂时,EF在创建最优化的SQL查询方面一直没有做得很好。 I can't answer how you get Linq to understand your database optimization design; 我无法回答你如何让Linq了解你的数据库优化设计; however, if you know the SQL that should be generated, then I am proposing that for better complex READ performance, use a NuGet package like Dapper to write the exact SQL you want and get the joins your database deserves. 但是,如果您知道应该生成的SQL,那么我建议为了更好的复杂READ性能,使用像Dapper这样的NuGet包来编写您想要的确切SQL并获得数据库应该得到的连接。

using Dapper;

[...]

public class SalsaZima
{
  public int ID { get; set; }
  public string MySalsaColumn { get; set; }
  public string MyZimaColumn { get; set; }
}

[...]

// get data from database
using (IDbConnection dp = Dapper)
{
  string query = @"SELECT 
                     s.ID
                    ,s.MySalsaColumn
                    ,z.MyZimaColumn
                   FROM dbo.Salsa s 
                   LEFT JOIN dbo.Zima z
                     ON s.ZimaID = z.ID";

  return dp.Query<SalsaZima>(query).ToList();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM