在我的数据库中有Document
和DocumentFile
个表。主键 - 列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使用简单的查询计划,比如查询带有评论条件?
答案 0 :(得分:3)
这是因为默认情况下EF为空值模仿.net语义。也就是说:如果一个字符串有一个值,它永远不会等于null:
stringValue != null
...评估为真。
在SQL语义中,这个等式是未定义的。如果用作谓词,永远不会产生任何结果。 (与正确的语法相反:stringValue IS NOT NULL
)。即使stringValue
为null
,在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();
第一个查询为我提供Code
为null
的公司。第二个查询......无。
从执行的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
未定义,查询不会返回任何结果。
所以你可以转向数据库空语义,但这是一个谨慎的决定。如果空值不起作用(即总是会在非空值之间进行比较),则可以安全地进行。