实体框架数据库查询速度很慢

时间:2015-12-26 14:31:35

标签: sql sql-server entity-framework tsql

在我的数据库中有DocumentDocumentFile个表。主键 - 列Uid(在两个表中)。 DocumentFile通过列Document引用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使用简单的查询计划,比如查询带有评论条件?

1 个答案:

答案 0 :(得分:3)

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

stringValue != null

...评估为真。

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

您可以告诉EF使用SQL null语义,但让我们看一个简单的示例,它会如何导致意外结果。我在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

VS

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

有,WHERE [Extent1].[Code] = @p__linq__0未定义,查询不会返回任何结果。

所以你可以转向数据库空语义,但这是一个谨慎的决定。如果空值不起作用(即总是会在非空值之间进行比较),则可以安全地进行。