来自 Management Studio 与 EF Core 5 的 SQL 服务器查询的不同性能

Different performance from SQL Server query from Management Studio vs EF Core 5

我写了一个简单的 EF Core 查询,它使用一些 where 子句在 table 上创建 select 来过滤数据:实际日期和字段之间的开始日期和结束日期 (DescrizioneCommessa)包含一个值。

        var query = _ctx.Commessas
            .Where(x => (x.DataInizioCommessa.HasValue && x.DataInizioCommessa <= DateTime.Now) || !x.DataInizioCommessa.HasValue)
            .Where(x => (x.DataFineCommessa.HasValue && x.DataFineCommessa >= DateTime.Now) || !x.DataFineCommessa.HasValue)
            .Where(x => x.DescrizioneCommessa.Contains(pattern))
            .OrderBy(x => x.DescrizioneCommessa);

为了得到原始的 SQL 我只是执行语句:

            var sql = facis.ToQueryString();

结果查询是:

DECLARE @__pattern_0 nvarchar(50) = N'COMUNE';

SELECT *
FROM [Commessa] AS [c]
WHERE (([c].[DataInizioCommessa] IS NOT NULL AND [c].[DataInizioCommessa] <= GETDATE()) OR [c].[DataInizioCommessa] IS NULL) 
       AND (([c].[DataFineCommessa] IS NOT NULL AND ([c].[DataFineCommessa] >= GETDATE())) OR [c].[DataFineCommessa] IS NULL)
       AND ((@__pattern_0 LIKE N'') OR (CHARINDEX(@__pattern_0, [c].[DescrizioneCommessa]) > 0))
ORDER BY [c].[DescrizioneCommessa]

我注意到与手写版本相比,执行查询需要很长时间:

SELECT *
FROM Commessa
WHERE (DescrizioneCommessa LIKE '%COMUNE%') 
      AND (DataInizioCommessa <= GETDATE() OR DataInizioCommessa IS NULL) 
      AND (DataFineCommessa >= GETDATE() OR DataFineCommessa IS NULL);

EF 查询甚至需要一分钟以上的时间来详细说明,而正常查询是即时的。

我确认问题出在 where 子句的这一部分:

   AND ((@__pattern_0 LIKE N'') OR (CHARINDEX(@__pattern_0, [c].[DescrizioneCommessa]) > 0))

如果我将上面的行替换为:

   AND (DescrizioneCommessa LIKE '%COMUNE%') 

问题已解决,性能最优

为什么这条线

.Where(x => x.DescrizioneCommessa.Contains(pattern))

造成这个问题?

这是在 EF.Core 中记录的行为,如 SO 所讨论:

作为一般命题,LIKE 表达式不适用于查询优化器。在较大的数据集中,这成为一个严重的问题,即使它们在小得多的未优化数据集中工作得很好。优化在很大程度上取决于匹配的模式以及它是否是范围查找。在您的情况下,该模式无法使用索引,EF 只是试图将其转换为 可能 可索引的表达式,在这种情况下,在 运行 之后的表达式就足够了数据库洞察引擎会建议您实施适当的索引。

Read about some other discussions about parsing String.Contains() to SQL in git hub: https://github.com/dotnet/efcore/issues/474

当您明确想要使用 SQL LIKE 时,EF Core 添加了 EF.Functions.Like():

var query = _ctx.Commessas
            .Where(x => (x.DataInizioCommessa.HasValue && x.DataInizioCommessa <= DateTime.Now) || !x.DataInizioCommessa.HasValue)
            .Where(x => (x.DataFineCommessa.HasValue && x.DataFineCommessa >= DateTime.Now) || !x.DataFineCommessa.HasValue)
            .Where(x => EF.Functions.Like(x.DescrizioneCommessa, pattern)
            .OrderBy(x => x.DescrizioneCommessa);