LINQtoSQL 生成的 SQL 中的外连接过多

Posted

技术标签:

【中文标题】LINQtoSQL 生成的 SQL 中的外连接过多【英文标题】:Too many outer joins in LINQtoSQL generated SQL 【发布时间】:2010-10-19 19:57:59 【问题描述】:

我有一个关于 LINQ2SQL 查询生成的 SQL 语句的问题。我有两个数据库表(VisibleForDepartmentId 是外键):

AssignableObject                 Department
----------------------           ------------
AssignableObjectId        ┌────> DepartmentId
AssignableObjectType      │
VisibleForDepartmentId ───┘

还有如下映射信息(注意AssignableObject是抽象的):

<Database Name="SO_755661" Class="DataClassesDataContext">
  <Table Name="dbo.AssignableObject" Member="AssignableObjects">
    <Type Name="AssignableObject" Modifier="Abstract">
      <Column Name="AssignableObjectId" Type="System.Int32"
              DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true"
              IsDbGenerated="true" CanBeNull="false" />
      <Column Name="AssignableObjectType" Type="System.String"
              DbType="VarChar(50) NOT NULL" CanBeNull="false"
              AccessModifier="Private" IsDiscriminator="true"/>
      <Column Name="VisibleForDepartmentId" Type="System.Int32"
              DbType="Int" CanBeNull="true" />
      <Association Name="Department_AssignableObject" Member="VisibleForDepartment"
                   ThisKey="VisibleForDepartmentId" OtherKey="DepartmentId"
                   Type="Department" IsForeignKey="true" />
      <Type Name="Asset" InheritanceCode="Asset" IsInheritanceDefault="true" />
      <Type Name="Role" InheritanceCode="Role" />
    </Type>
  </Table>
  <Table Name="dbo.Department" Member="Departments">
    <Type Name="Department">
      <Column Name="DepartmentId" Type="System.Int32"
              DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true"
              IsDbGenerated="true" CanBeNull="false" />
      <Column Name="Name" Type="System.String" DbType="VarChar(50) NOT NULL"
              CanBeNull="false" />
      <Association Name="Department_AssignableObject" Member="AssignableObjects"
                   ThisKey="DepartmentId" OtherKey="VisibleForDepartmentId"
                   Type="AssignableObject" />
    </Type>
  </Table>
</Database>

还有如下代码:

var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Asset>(a => a.VisibleForDepartment);
dataContext.LoadOptions = loadOptions;
var assets = from a in dataContext.Assets
             select a;

这会导致一个带有两个相同左外连接的 SQL 查询:

SELECT t0.AssignableObjectType, t0.AssignableObjectId, t0.VisibleForDepartmentId,
       t2.test, t2.DepartmentId, t2.Name, t4.test AS test2,
       t4.DepartmentId AS DepartmentId2, t4.Name AS Name2
FROM dbo.AssignableObject AS t0
LEFT OUTER JOIN (
    SELECT 1 AS test, t1.DepartmentId, t1.Name
    FROM dbo.Department AS t1
    ) AS t2 ON t2.DepartmentId = t0.VisibleForDepartmentId
LEFT OUTER JOIN (
    SELECT 1 AS test, t3.DepartmentId, t3.Name
    FROM dbo.Department AS t3
    ) AS t4 ON t4.DepartmentId = t0.VisibleForDepartmentId

为什么有两个外连接,一个就足够了?

亲切的问候,

罗纳德

【问题讨论】:

你能发布你的对象定义吗?我想也许你应该做一个 .LoadWith(x => x.Department)? 【参考方案1】:

我发现了导致这些重复外连接的原因。它们发生在持久类被两个或多个子类继承时。如果您使用LoadWith,则对于每个子类,都会在生成的 SQL 语句中添加一个新的外连接。

在我的示例中,AssignableObject 有两个子类:AssetRole。这将导致与Department 表的两个外连接。如果我添加另一个子类,则会添加第三个外连接。

我不确定 SQL Server 是否足够聪明,可以意识到外部连接是重复的。我在 Microsoft Connect 上有 posted this。

编辑:显然我的问题与another issue 重复,并且不会在下一个版本的 LINQ2SQL 中修复。

【讨论】:

【参考方案2】:

您是否不小心在数据库中定义了 2 个外键关系 相同的 2 个表上的相同的 2 列?

【讨论】:

那简直太简单了 :) 但事实并非如此。我在两张桌子之间只有一个外国人。 您能否发布这 2 个表的表定义,因为当我尝试使用 2 个示例表(其中 Dept.DeptID 是 PK 而 Asset.DeptID 是 FK)时,我会得到一个内部联接,即我的期望 嗯,我的例子可能过于简单了......在现实世界中,这些表有更多的列。但我可能遗漏了一些其他信息。不同的一点是 Asset 是抽象持久基类的子类。我会尝试发布更多信息。 我想出了一个生成重复外连接的示例。将在十五分钟内编辑我的原始答案。【参考方案3】:

您可以尝试在查询本身中执行左外连接。我不确定生成了什么 SQL,因为我这里没有你的数据库。

var assets = from a in dataContext.Assets
             join d in dataContext.Departments on 
                  a.VisibleForDepartmentId equals d.DepartmentId
                  into temp
             from t in temp.DefaultIfEmpty()
             select a;

【讨论】:

【参考方案4】:

我在 LINQPad 中使用其默认数据库创建了一个类似的查询

var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Products>(a => a.Category);
LoadOptions = loadOptions;

var products = from a in Products
               select a;

products.Dump();

得到

SELECT [t0].[ProductID], [t0].[ProductName], [t0].[CategoryID], [t2].[test], [t2].[CategoryID] AS [CategoryID2], [t2].[CategoryName]
FROM [Products] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[CategoryID], [t1].[CategoryName]
    FROM [Categories] AS [t1]
    ) AS [t2] ON [t2].[CategoryID] = [t0].[CategoryID]

按预期只使用了一个 OUTER JOIN。

【讨论】:

“1 AS [test]”在内部选择中做了什么? 这个查询是LINQ to SQL自动生成的,所以只能猜测。

以上是关于LINQtoSQL 生成的 SQL 中的外连接过多的主要内容,如果未能解决你的问题,请参考以下文章

sql条件下解码中的外连接

通过表和其他表执行搜索与 SQL Server 中的外键连接的内容以及如何对数据进行排序 [关闭]

将“额外数据”添加到 LINQ to SQL 生成的实体?

Linq to Sql:多个左外连接

使用DISTINCT时,LINQ to SQL不生成ORDER BY?

LINQ to SQL [npgsql] 从 SelectMany 生成不正确的查询