【问题标题】:How to force entity framework not to query as sp_executesql如何强制实体框架不查询为 sp_executesql
【发布时间】:2013-04-12 11:48:52
【问题描述】:

我有一个包含 100 亿行的表。当我使用 where clausule 查询并且值是硬编码的(tabulka = tabulka.Where(x => x.Value.Contains("value") sql 查询作为批处理发送到 sql。大约需要 5 秒。当值没有硬编码时(tabulka = tabulka.Where(x => x.Value.Contains(value)),查询发送一个 RPC 和耗时 15 秒。

这是 EF 发送给 SQL 的真实示例(来自 Profiler):

exec sp_executesql N'SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Tabulka] AS [Extent1]
    WHERE ([Extent1].[Column] LIKE @p__linq__0 ESCAPE N''~'') AND ([Extent1].[Column] LIKE @p__linq__1 ESCAPE N''~'')
)  AS [GroupBy1]',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'%text1%',@p__linq__1=N'%text2%'

这需要 15 秒。

当我添加 OPTION (RECOMPILE) 需要 5 秒:

exec sp_executesql N'SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Tabulka] AS [Extent1]
    WHERE ([Extent1].[Column] LIKE @p__linq__0 ESCAPE N''~'') AND ([Extent1].[Column] LIKE @p__linq__1 ESCAPE N''~'')
)  AS [GroupBy1] OPTION (RECOMPILE)',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'%text1%',@p__linq__1=N'%text2%'

改写成简单查询也需要5秒:

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Tabulka] AS [Extent1]
    WHERE ([Extent1].[Column] LIKE '%text1%' AND [Extent1].[Column] LIKE '%text2%')
)  AS [GroupBy1]

问题是如何强制 EF 将其作为 Batch 发送或执行某些操作以花费 5 秒而不是 15 秒。

这些查询仅作为演示,真正的查询可能要复杂得多,我不知道要重建它而不使用 IQueryable。

我们将不胜感激。 Here are execution plans for queries

【问题讨论】:

    标签: c#-4.0 entity-framework-5


    【解决方案1】:

    我可以想出几种方法来解决它,但从 EF 6.2 及更早版本开始,没有标准/内置方法可以更改 EF 创建查询以添加提示/选项的方式。您面临的问题是由于查询计划效率低下,该查询计划被保留在统计信息中,并且在您使用另一个值调用它时被重新使用。我会首先尝试弄清楚为什么要创建错误的查询计划,也许有比操纵代码更好的方法来解决它。为此,请尝试在 Ms Sql 论坛中提出问题(减去 EF 部分),并包括查询、架构详细信息和您的执行计划,就像您在本文中所做的那样。

    如果您的 nvarchar/varchar 列在 DbContext 映射中设置为正确的编码 (ascii/unicode),请检查您的映射。

    可选的解决方法

    1. 使用字符串构建器以传统/旧方式构建查询。一旦有了查询,就使用 SqlQuery 函数在 DbContext 上的 DbSet 上执行它。这使您可以完全控制查询的构建以及包含的内容,但存在明显的缺点,例如无法使用 Linq-to-Sql 进行查询构建。
    2. 创建一个存储过程并将其映射到 EF。在存储过程中,您可以根据需要构建查询。存储过程的结果可能是一个或多个副作用(如果我没记错的话,也是一个表)并使用 EF 映射回一个对象。
    3. 挂钩到 DbContext 并在执行之前捕获已构建的 sql,然后操作 Sql 以添加您的选项语句。您可以使用 IDbCommandInterceptor 接口的自定义实现来执行此操作,然后重新编写 sql 以包含 OPTION 语句以重新生成执行计划。我会将此作为最后的手段。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-19
      • 1970-01-01
      • 1970-01-01
      • 2021-12-25
      • 2013-03-17
      • 2014-02-24
      • 1970-01-01
      相关资源
      最近更新 更多