【问题标题】:Entity Framework database query very slow实体框架数据库查询很慢
【发布时间】:2015-12-26 14:31:34
【问题描述】:

在我的数据库中有DocumentDocumentFile 表。主键 - 列 Uid(在两个表中)。 DocumentFile 通过列 DocumentUid 引用 Document

我知道文档文件的 uid,我想选择带有文件的文档(左连接),EF 生成这个查询:

DECLARE @p__linq__0 uniqueidentifier,@p__linq__1 uniqueidentifier,@p__linq__2 varchar(max) ,@p__linq__3 nvarchar(max) ,@p__linq__4 uniqueidentifier

SELECT @p__linq__0=NULL,@p__linq__1=NULL,@p__linq__2=NULL,@p__linq__3=NULL,@p__linq__4='8670AD28-9FA6-41F3-94B9-6B91FD2AE110'

SELECT 
*
FROM  [dbo].[Document] AS [Extent1]
LEFT OUTER JOIN [dbo].[DocumentFile] AS [Extent2] ON [Extent1].[Uid] = [Extent2].[DocumentUid]
WHERE ((([Extent1].[EntityUid] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)) OR (@p__linq__1 IS NULL)) 
        AND ((([Extent1].[EntityTypeCode] = @p__linq__2) AND ( NOT ([Extent1].[EntityTypeCode] IS NULL OR @p__linq__2 IS NULL))) OR (([Extent1].[EntityTypeCode] IS NULL) AND (@p__linq__2 IS NULL)) OR (@p__linq__3 IS NULL) OR (( CAST(LEN(@p__linq__3) AS int)) = 0)) 
        AND ((([Extent2].[Uid] = @p__linq__4) AND ( NOT ([Extent2].[Uid] IS NULL OR @p__linq__4 IS NULL))) OR (([Extent2].[Uid] IS NULL) AND (@p__linq__4 IS NULL)) )

(用星号替换的一长列列和顶部声明的参数,但没关系)

对于复杂的查询计划(约 20 秒),此查询的运行速度非常慢。如果我在查询结束时评论此条件:

 /*OR (([Extent2].[Uid] IS NULL) AND (@p__linq__4 IS NULL))*/

它以闪电般的速度运行(几毫秒)。 Extent2 是 DocumentFile,列 Uid 是主键,它永远不会是 NULL

在 C# 代码列 Uid 声明为 Guid:

public class DocumentFile
{
    public const string EntityType = "DocumentFile";

    [Key]
    public Guid Uid { get; set; }

    public Guid DocumentUid { get; set; }
    ...
}

如何修复查询或告诉 SQL Server 使用简单的查询计划,例如带有注释条件的查询?

【问题讨论】:

  • 如果要选择带文件的文档,为什么要使用外连接而不是内连接?
  • 在某些情况下文档可能是空的,文档中没有文件。但我想选择文档本身。
  • 您的 LINQ 查询是什么?也许这可以解释为什么它会为键列生成空检查。如果该列在数据库中被定义为可为空(真的不知道键列如何/为什么为空),也许您可​​以将 RequiredAttribute 添加到 Uid 以告诉 EF 该列不能null,也许这样它会从生成的 sql 语句中删除 null 检查。
  • RequiredAttribute 不会改变任何东西。 Linq 查询并不简单,它在某些地方进行了修改-添加了条件。如果找不到解决方案,我稍后会编辑我的帖子。
  • 这个问题我没仔细看但是让我想起了stackoverflow.com/a/18952559/73226

标签: sql sql-server entity-framework tsql


【解决方案1】:

这是因为 EF 默认模仿空值的 .net 语义。也就是说:如果一个字符串有一个值,它永远不会等于 null:

stringValue != null

...计算结果为真。

在 SQL 语义中,这个等式是未定义的。如果用作谓词,它 never 会产生任何结果。 (与正确的语法相反:stringValue IS NOT NULL)。即使stringValuenull,在SQL 中,stringValue = null 也不会评估为真!

您可以告诉 EF 使用 SQL 空语义,但让我们看一个简单的示例,这会如何导致意外结果。我在 Linqpad 中连接了一个上下文,并使用此代码来比较两种语义:

string code = null;
this.Configuration.UseDatabaseNullSemantics = false; // the default
Companies.Where(c => c.Code == code).Dump();

this.Configuration.UseDatabaseNullSemantics = true;
Companies.Where(c => c.Code == code).Dump();

第一个查询给出了Codenull 的公司。第二个查询...没有。

从执行的 SQL 语句中可以看出原因:

-- Region Parameters
DECLARE @p__linq__0 VarChar(1000) = null
-- EndRegion
SELECT ...
    FROM [dbo].[Company] AS [Extent1]
    WHERE ([Extent1].[Code] = @p__linq__0)
       OR (([Extent1].[Code] IS NULL) AND (@p__linq__0 IS NULL))
GO

SELECT ...
    FROM [dbo].[Company] AS [Extent1]
    WHERE [Extent1].[Code] = @p__linq__0

就是这样,WHERE [Extent1].[Code] = @p__linq__0 未定义,查询没有返回任何结果。

因此,您可以转向数据库空语义,但这是一个需要谨慎做出的决定。如果空值不起作用(即非空值之间总是存在比较),您可以安全地进行。

【讨论】:

  • 好的,现在我明白了。我不会启用数据库空语义。如果文件 uid 传入参数,我将查询更改为内部联接。如果只传递文件 uid 而没有文件 uid,我使用左连接。似乎工作正常!
猜你喜欢
  • 2014-12-13
  • 2023-03-05
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多