【发布时间】:2016-08-23 21:09:31
【问题描述】:
背景:我正在开发一个系统来清理内部客户列表并找出联系人的电子邮件地址,我们已经在该公司拥有其他人的电子邮件地址。为了做到这一点,我有(简化的)3 个表:
联系人:
ID
CompanyId
Email
Domain
电子邮件域:
ID
Domain
EmailFormat
EmailFormatConfirmed
我有一个手动例程,它是说,给定公司,找到我的下一个联系人,我们有他们的域名但没有他们的电子邮件地址:
int companyId = 53;
var emails = Contacts.Where(p => p.companyId == companyId
&& p.Email == null
&& !string.IsNullOrEmpty(p.Domain)).Select(p => p.Domain);
var domain =
EmailDomains.FirstOrDefault(
d => !d.EmailFormatConfirmed
&& !string.IsNullOrEmpty(d.Domain)
&& emails.Contains(d.Domain));
此查询运行非常缓慢,并且在检查生成的 Sql 时:
-- Region Parameters
DECLARE @p__linq__0 Int = 53
-- EndRegion
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Domain] AS [Domain],
[Extent1].[EmailFormat] AS [EmailFormat],
[Extent1].[EmailFormatConfirmed] AS [EmailFormatConfirmed],
FROM [dbo].[EmailDomain] AS [Extent1]
WHERE ([Extent1].[EmailFormatConfirmed] <> 1)
AND ( NOT (([Extent1].[Domain] IS NULL) OR ((LEN([Extent1].[Domain])) = 0)))
AND ( EXISTS (
SELECT 1 AS [C1]
FROM [dbo].[Contacts] AS [Extent2]
WHERE ([Extent2].[CompanyId] = @p__linq__0)
AND ([Extent2].[Email] IS NULL)
AND ( NOT (([Extent2].[Domain] IS NULL) OR ((LEN([Extent2].[Domain])) = 0)))
AND (([Extent2].[Domain] = [Extent1].[Domain]) OR (([Extent2].[Domain] IS NULL) AND ([Extent1].[Domain] IS NULL)))
))
我可以看到有问题的部分是存在子句末尾的OR (([Extent2].[Domain] IS NULL) AND ([Extent1].[Domain] IS NULL))。为什么会出现这种情况?我无法理解它是如何有效的,如果我是手动编写 Sql (目前这是我将不得不回退的)它不会。我错过了一些明显的东西吗?删除它会使查询运行得非常快(正如预期的那样 - 这里有很多有效地交叉连接的空域)
【问题讨论】:
-
有必要处理
NULL的情况,因为在SQL 中,NULL != NULL。所以这导致value1 = value2 OR (value1 IS NULL AND value2 IS NULL)。 -
@Maarten - 使用 EF6。但是我不确定您是否遇到了问题 - 我可以在 Sql 中将其编写为 CTE 或子查询,它将检索指定公司的联系域,没有电子邮件,然后将其内部加入到 EmailDomains 表中。我理解 null != null 但是我根本不想考虑空值
标签: c# entity-framework linq