Entity Framework 6 - 参数查询比内联参数查询慢 11 倍。为什么?

Entity Framework 6 - Parameter query 11x slower than inline parameters query. Why?

分析我们产品中的一些查询,我发现使用 Entity Framework 6 个参数会影响这一查询的性能。关于这个话题很多,大家意见不一。

在我的测试用例中,这两个查询是相同的,除了我将参数内联 SQL.

DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE

SET STATISTICS TIME ON 

exec sp_executesql N'SELECT DISTINCT 
    [Extent1].[POSTerminalID] AS [POSTerminalID]
    FROM [dbo].[POSDataEvents] AS [Extent1]
    WHERE ([Extent1].[DataTimeStamp] <= @p__linq__0) AND ([Extent1].[DataTimeStamp] >= @p__linq__1) AND ([Extent1].[DataOwnerID] = @p__linq__2)
',N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7),@p__linq__2 smallint',@p__linq__0='2017-06-22 16:16:01.3570000',@p__linq__1='2017-04-23 04:00:00',@p__linq__2=1

exec sp_executesql N'SELECT DISTINCT 
    [Extent1].[POSTerminalID] AS [POSTerminalID]
    FROM [dbo].[POSDataEvents] AS [Extent1]
    WHERE ([Extent1].[DataTimeStamp] <= ''2017-06-22 16:16:01'') AND ([Extent1].[DataTimeStamp] >= ''2017-04-23 04:00:00'') AND ([Extent1].[DataOwnerID] = 1)'

输出统计数据:

 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(289 row(s) affected)

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 11859 ms,  elapsed time = 5827 ms.

 SQL Server Execution Times:
   CPU time = 11859 ms,  elapsed time = 5828 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(289 row(s) affected)

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 6221 ms,  elapsed time = 509 ms.

 SQL Server Execution Times:
   CPU time = 6221 ms,  elapsed time = 509 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

生成的查询执行计划也略有不同,但仍然访问相同的表。

这让我想到了我的问题:

  1. 为什么仅用参数表示的相同查询会慢近 11 倍(约 6 秒到 .5 秒)?结果使用相同的索引。

  2. 如何在 entity Framework 中强制内联参数?几个月前,我看到另一位用户在 post 上询问 SO,但没有任何回应。我不确定这是否是正确的答案,但想测试一下。我们不会写原始的 SQL 内联。它必须来自 Entity Framework.

问题是 Entity Framework 生成 DateTime2 类型的参数,而实际的数据库列定义为 DateTime。有两种解决方案:

要么将数据库列更改为 DateTime2,要么告诉 Entity Framework 使用 DateTime (see here)。

我遇到过 可选 包括昂贵的文本字段的情况。

因此生成的代码类似于

WHEN @includeExpensiveField = 1 THEN [o].[ExpensiveField] ELSE NULL

所以在 SSMS 中,当我 运行 手动查询时,我只是将其更改为

WHEN 0 = 1

它完全优化了那个字段。

然而,参数化查询必须在计划中考虑到它,当我在 SSMS > 查询存储中找到查询时,我可以看到它总是在扫描和加载昂贵的字段,因为它无法优化它。

注意:我使用了这段代码

SELECT Txt.query_text_id, Txt.query_sql_text, Pl.plan_id, Qry.* FROM sys.query_store_plan AS Pl INNER JOIN sys.query_store_query AS Qry
ON Pl.query_id = Qry.query_id INNER JOIN sys.query_store_query_text AS Txt
ON Qry.query_text_id = Txt.query_text_id where query_sql_text not like '%expensivefield%' order by last_execution_time desc

找到使用的 query_id 然后在 SSMS > Query Store > Tracked Queries

中找到实际执行的查询计划