我被要求调查数据库中一个性能不佳的查询的原因。我确定它是由LINQ语句生成的,并跟踪它到源代码,并将其加载到linqpad中。在Linqpad中,我显示了生成的SQL,如下所示。正如您所看到的,WHERE子句的第一部分是不必要的,并且通过避免索引大大降低了查询速度。它应该只查询DocumentStorageId键,就是这样。没有指向IN()语句的点,产品表中的每一行都有这些值之一,并且不为空。关于我如何改变我的linq语句,使ID是第一个,并通过索引命中的想法?
void Main()
{
var uow = new UnitOfWork(this);
var repo = new Repository<Product>(this,uow);
var documentStorageId = new Guid("473BAE6B-A1A1-49BE-9FD5-AB6B870A82B1");
var result = repo.Queryable()
.Where(x => x.DocumentStorageId == documentStorageId)
.FirstOrDefault();
result.Dump();
}
生成SQL输出:
SELECT
[Extent1].[AColumn],
[Extent1].[BColumn]
FROM [dbo].[Product] AS [Extent1]
WHERE
([Extent1].[ProductType] IN
(N'Type1',N'Type2',N'Type3',N'Type4',N'Type5',N'Type6'))
AND ([Extent1].[DocumentStorageId] = @p__linq__0)
编辑:为了进一步澄清,模型是使用代码优先创建的。Product是一个基类。有6种衍生类型的产品(Type1, Type2等)。ProductType是标识符列。因此,EF似乎试图包括所有可能的产品类型,但为什么要这么做呢?包含all与不指定特定的all是一样的,并且IN()子句会使查询执行得很慢。
对于像您正在做的这样的TPH类型查询,EF需要找出您感兴趣的类型。它不知道它有一个完整的类型列表,只知道当您请求应用程序中的根类型时,派生类型列表如下所示。这意味着EF必须包含类型列表以避免问题。
有两种方法可以解决这个问题。
- 在你的上下文中绑定一个非tph DbSet并使用它。
- 在(ProductType, DocumentStorageId)上添加索引,以便在这里可以快速查找
产品有6种派生类型(Type1、Type2等)。ProductType是标识符列。
你知道这个…但是EF没有办法知道这一点。据它所知,你有20种产品类型,但只告诉EF这6种。想象一下,当你要了所有的时候,它却给了你20个,尽管你只为6个编程了。
为什么这个实体框架LINQ查询生成一个缓慢的where子句?
因此,为了使查询按预期返回,此处使用in子句。(这也是我更喜欢TPT而不是TPH的原因之一)。