Sql 实体框架数据库查询非常慢
在我的数据库中有Document和DocumentFile表。主键-两个表中的列Uid。DocumentFile通过DocumentUid列引用文档 我知道文档文件的uid,我想选择具有左联接文件的文档,EF生成此查询: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
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,则使用左连接。看起来不错!