【问题标题】:Include and Where predicate cause left join instead of inner joinInclude 和 Where 谓词导致左连接而不是内连接
【发布时间】:2012-07-21 07:20:32
【问题描述】:

具有以下表结构(删除了无关列)

create table [Events]
(
    ID int not null identity,
    Name nvarchar(128) not null,
    constraint PK_Events primary key(ID)
)

create table [Donations]
(
    ID int not null identity,
    EventID int not null,
    Amount decimal(10, 2) not null,

    constraint PK_Donations primary key(ID),
    constraint FK_Donations_Events foreign key(EventID) references [Events](ID) on update no action on delete no action
)

我使用以下 Linq-to-Entities 查询:

// 1
ents.Donations.Where(d => d.Amount > 25.0m && d.Event.Name.Contains("Run")).ToList();

// 2
ents.Donations.Include("Event").Where(d => d.Amount > 25.0m).ToList();

// 3
ents.Donations.Include("Event").Where(d => d.Amount > 25.0m && d.Event.Name.Contains("Run")).ToList();

产生(来自 SQL Profiler):

-- 1
SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[EventID] AS [EventID], 
[Extent1].[Amount] AS [Amount]
FROM  [dbo].[Donations] AS [Extent1]
INNER JOIN [dbo].[Events] AS [Extent2] ON [Extent1].[EventID] = [Extent2].[ID]
WHERE ([Extent1].[Amount] > 25.0) AND ([Extent2].[Name] LIKE N'%Run%')

-- 2
SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[EventID] AS [EventID], 
[Extent1].[Amount] AS [Amount], 
[Extent2].[ID] AS [ID1], 
[Extent2].[Name] AS [Name]
FROM  [dbo].[Donations] AS [Extent1]
INNER JOIN [dbo].[Events] AS [Extent2] ON [Extent1].[EventID] = [Extent2].[ID]
WHERE [Extent1].[Amount] > 25.0

-- 3
SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[EventID] AS [EventID], 
[Extent1].[Amount] AS [Amount], 
[Extent3].[ID] AS [ID1], 
[Extent3].[Name] AS [Name]
FROM   [dbo].[Donations] AS [Extent1]
INNER JOIN [dbo].[Events] AS [Extent2] ON [Extent1].[EventID] = [Extent2].[ID]
LEFT OUTER JOIN [dbo].[Events] AS [Extent3] ON [Extent1].[EventID] = [Extent3].[ID]
WHERE ([Extent1].[Amount] > 25.0) AND ([Extent2].[Name] LIKE N'%Run%')

为什么在第三个查询中,它第二次在Events 表上生成LEFT OUTER JOIN?虽然查询产生了正确的结果,但看起来很奇怪,为什么 EF / LINQ 不能在 SELECTWHERE 子句中重用 [Extent2],为什么它是 LEFT OUTER JOIN

我正在使用 Visual Studio 2010 sp1 .NET 4,并且正在连接到 Sql Server 2008 Express。

【问题讨论】:

    标签: c# linq entity-framework linq-to-entities


    【解决方案1】:

    左连接是为了确保在捐赠指向不存在的事件的情况下,捐赠表中不会丢失任何行。他们不希望 Include 关键字具有导致原始表中缺少行的副作用,因此他们必须使用左连接以确保安全。

    关于将表格包含两次,这可能只是 EF 的限制。您在查询中提到它两次,它不够聪明,无法进行优化。

    我不得不说,如果你想优化SQL然后写SQL,不要打扰EF。您正在做的事情可以比作反编译 C# 并询问为什么汇编程序没有特定的优化。如果您使用 EF,那么请不要对它产生的 SQL 视而不见 :-)

    【讨论】:

    • 第二个查询使用Include,但不会产生左连接。
    • 很好的问题,我没有注意到。这对我来说似乎是前后矛盾的。第二个查询应该有一个左连接,这样 Include 就不会产生副作用,第三个查询应该有一个内连接,因为它无论如何都需要一个事件行。
    • 如果它是一个可以为空的列,或者我没有参照完整性,那么左连接将是有意义的。
    • 你有没有看到这个问题:stackoverflow.com/questions/7484249/…
    • 我现在有了,这似乎准确地描述了它。
    【解决方案2】:

    不是直接回答您的问题,而是在阅读您的 cmets 以获取其他答案后尝试为您指出正确的方向:

    您拥有保护某些 ORM 用法(包括 EF)所需的一切——这就是您所说的关于 SP 的数量和质量的全部内容。如果 sql 写得不好或难以维护,任何方法都会有问题,包括纯 sql。

    所以,如果某些ORM(EF等)有时会产生效率不高的代码,而这确实会导致性能问题,这就变成了“需求”,需要解决,即使使用SP也是如此。

    所以,从业务角度来看您的问题 - 您的存储过程结构糟糕且难以维护。您的团队中的大多数人可能是 C# 而不是 SQL 开发人员。

    使用 ORM 将提高代码库的可维护性,并允许更好地利用所有团队成员的 C# 专业知识。

    ORM 在某些特定场合产生的糟糕的 SQL 代码对于使用该技术几乎是“不可行的”,除非证明它会产生比解决现有问题更多的问题。

    【讨论】:

    • 我会反过来看。如果开发人员在 sql 方面缺乏经验,那么他们需要变得有经验。毕竟他们正在编写数据库应用程序,而如今缺乏 sql 知识是不可接受的。他们不需要的一件事是避免使用 sql 的更多理由。
    • 他们在 SQL 方面绝对不是没有经验,该应用程序已经经历了大约 9 年的不断开发(从 ASP.net 1.0 开始),许多不同的开发人员。现在的应用程序没有关注点分离,存储过程直接在后面的代码中调用。这就是为什么我正在寻找一种非常容易管理的东西,主要是因为我相信现在存储过程的组织是一种注销。
    猜你喜欢
    • 2020-06-06
    • 1970-01-01
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-15
    • 2015-03-04
    • 2018-07-31
    相关资源
    最近更新 更多