Sql 实体框架数据库查询非常慢

Sql 实体框架数据库查询非常慢,sql,sql-server,entity-framework,tsql,Sql,Sql Server,Entity Framework,Tsql,在我的数据库中有Document和DocumentFile表。主键-两个表中的列Uid。DocumentFile通过DocumentUid列引用文档 我知道文档文件的uid,我想选择具有左联接文件的文档,EF生成此查询: DECLARE @p__linq__0 uniqueidentifier,@p__linq__1 uniqueidentifier,@p__linq__2 varchar(max) ,@p__linq__3 nvarchar(max) ,@p__linq__4 uniqueid

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

我知道文档文件的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使用简单的查询计划,如用于带有注释条件的查询?

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

stringValue != null
。。。计算结果为true

在SQL语义中,这个等式是未定义的。如果用作谓词,它永远不会产生任何结果。与正确的语法相反:stringValue不为NULL。即使stringValue为null,在SQL中,stringValue=null也不会计算为true

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

字符串代码=null; this.Configuration.UseDatabaseNullSemantics=false;//默认值 companys.Wherec=>c.Code==Code.Dump; this.Configuration.UseDatabaseNullSemantics=true; companys.Wherec=>c.Code==Code.Dump; 第一个查询提供代码为空的公司。第二个问题。。。没有

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

-区域参数 声明@p\uuuu linq\uuuuu 0 VarChar1000=null -端区 选择 来自[dbo].[Company]作为[Extent1] 其中[Extent1].[Code]=@p\u linq\u 0 或[Extent1]。[Code]为NULL,@p__linq__0为NULL 去 vs

选择 来自[dbo].[Company]作为[Extent1] 其中[Extent1].[Code]=@p\u linq\u 0 就是这样,如果[Extent1].[Code]=@p\u linq\u\u 0未定义,则查询不会返回任何结果


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

如果要选择包含文件的文档,为什么要使用外部联接而不是内部联接?在某些情况下,文档可能为空,文档中没有文件。但我想选择文档本身。您的LINQ查询是什么?也许这可以解释为什么它会为键列生成空检查。如果该列在数据库中被定义为可空的,并且确实不知道键列如何/为什么会为空,那么您可以将RequiredAttribute添加到Uid中,告诉EF该列不能为空,这样它就可以从生成的sql语句中删除空检查。RequiredAttribute不会更改任何内容。Linq查询并不简单,它在某些地方进行了修改-添加了条件。如果我找不到解决方案,我会在以后编辑我的帖子。我没有详细看这个问题,但让我想起了Ok,现在我明白了。我不打算启用数据库空语义。如果文件uid传入参数,我将查询更改为内部联接。如果只传递文档uid而不传递文件uid,则使用左连接。看起来不错!