sql-server - 优化 Entity Framework 生成的 SQL Server 执行计划

标签 sql-server entity-framework linq-to-entities query-optimization

我有一个数据结构,基本上是一个带有标签字典的文档。我试图取回给定表单类型的所有文档,这些文档具有名为“姓氏”的标签和“史密斯”的标签值。可能有 0..N 个与文档关联的“姓氏”标签。

我正在使用以下 linq 查询来尝试将源文档与具有匹配标签的子文档进行匹配:

DB.Documents
    .Where(doc => doc.FormID == pd.IndexForm.FormID)
    .Where(doc => doc.Document_StringIndex_ReadOnly
                .Join(Fields,
                        dsi => new { FieldName = dsi.FieldName, FieldValue = dsi.StringValue },
                        dsi2 => new { FieldName = dsi2.FieldName, FieldValue = dsi2.StringValue },
                        (dsi, dsi2) => dsi.Document).Count() > 0);

使用 .ToTraceString() 输出时会生成以下查询

SELECT 
[Project1].*
FROM ( SELECT 
    [Extent1].*
    (SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM   [dbo].[Document_StringIndex_ReadOnly] AS [Extent2]
        INNER JOIN  (SELECT [Extent3].*
            FROM  [dbo].[Document] AS [Extent3]
            INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID] ) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL)))
        LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID]
        WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = @p__linq__7) AND ([Join1].[FieldName] = @p__linq__8)) AS [C1]
    FROM [dbo].[Document] AS [Extent1]
    WHERE [Extent1].[FormID] = @p__linq__5
)  AS [Project1]
WHERE [Project1].[C1] > 0

如果我直接用常量替换我的参数(如下所示),查询执行得非常快。但是,如果我保留参数,查询将需要几分钟的时间。

SELECT 
[Project1].*
FROM ( SELECT 
    [Extent1].*
    (SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM   [dbo].[Document_StringIndex_ReadOnly] AS [Extent2]
        INNER JOIN  (SELECT [Extent3].*
            FROM  [dbo].[Document] AS [Extent3]
            INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID] ) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL)))
        LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID]
        WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = 1015) AND ([Join1].[FieldName] = 'DDKey')) AS [C1]
    FROM [dbo].[Document] AS [Extent1]
    WHERE [Extent1].[FormID] = 22
)  AS [Project1]
WHERE [Project1].[C1] > 0

生成执行计划后,我了解到如果直接替换参数值,SQL Server会执行索引查找,并且查询速度很快。一旦我保留参数,SQL Server 将执行索引扫描,并且我的查询超时。有没有什么方法可以让 SQL Server 始终进行查找?我可以强制 Entity Framework 不使用参数化查询吗?

最佳答案

在生成的SQL中,这一行

[Join1].[FieldName] = @p__linq__8

可能是问题所在。

如果 FieldNamevarchar(...)@p__linq__8nvarchar(...)那么该子句将导致表扫描,因为参数类型与索引类型不匹配。

当您直接替换“DDKey”时,类型会匹配,因此您会获得索引查找。尝试使用 N'DDkey' 进行查询,看看是否进行表扫描。

这是各种版本的 Linq to Sql 和 Linq to Entities 的问题,但可能会在以后的版本中修复。

如果您无法更新到最新版本,解决该问题的一种方法是将 FieldName 更改为 nvarchar(...)

关于sql-server - 优化 Entity Framework 生成的 SQL Server 执行计划,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10855991/

相关文章:

c# - 我可以迁移到 Entity Framework 中没有迁移的情况下创建的数据库吗?

c# - 1对1的关系

asp.net-mvc - 如何从 Entity Framework 模型中删除对象而不先加载它?

c# - 从字符串构建表达式<Func<T,bool>>

c# - 尝试获取在 Linq2Entities 查询中连接的字符串

sql-server - 数据从 SQL Server 数据库中消失

sql-server - 单列(身份)表的用法?

entity-framework - 当我将 EF6 添加到解决方案时出现 "Could not parse configuration file..."

sql-server - 为什么 DELETE TOP(X) 语句不删除先前 SELECT TOP(X) 返回的相同记录?

SQL连接2个没有关系的表