LINQ 到 SQL。如何防止使用 Apply 运算符
Posted
技术标签:
【中文标题】LINQ 到 SQL。如何防止使用 Apply 运算符【英文标题】:Linq To SQL. How to prevent using Apply operator 【发布时间】:2012-12-13 13:15:47 【问题描述】:我有一个问题:
var contactInfos = from person in persons
join tempDesiredCar in desiredCars on person.contact_id equals tempDesiredCar.groupEntity_id
into tempDesiredCars
from desiredCar in tempDesiredCars.DefaultIfEmpty()
select new name = person.name, car = desiredCar.name;
此代码转换为 SQL:
SELECT [t1].[name], [t19].[name] AS [car]
FROM [dbo].[Person] AS [t1]
CROSS APPLY ((
SELECT NULL AS [EMPTY]
) AS [t6]
OUTER APPLY (
SELECT [t18].[name]
FROM (
SELECT [t17].[contact_id], [t17].[name]
FROM (
SELECT [t7].[contact_id], [t11].[name]
FROM [dbo].[DesiredCar] AS [t7]
INNER JOIN (
SELECT MAX([t9].[value]) AS [value]
FROM (
SELECT [t8].[id] AS [value], [t8].[contact_id]
FROM [dbo].[DesiredCar] AS [t8]
) AS [t9]
GROUP BY [t9].[contact_id]
) AS [t10] ON ([t7].[id]) = [t10].[value]
LEFT OUTER JOIN [dbo].[FamilyModel] AS [t11] ON [t11].[id] = [t7].[model]
WHERE [t7].[model] IS NOT NULL
UNION
SELECT [t12].[contact_id], [t16].[name]
FROM [dbo].[DesiredCar] AS [t12]
INNER JOIN (
SELECT MAX([t14].[value]) AS [value]
FROM (
SELECT [t13].[id] AS [value], [t13].[contact_id]
FROM [dbo].[DesiredCar] AS [t13]
) AS [t14]
GROUP BY [t14].[contact_id]
) AS [t15] ON ([t12].[id]) = [t15].[value]
LEFT OUTER JOIN [dbo].[CarBrand] AS [t16] ON [t16].[id] = [t12].[carBrand_id]
WHERE [t12].[carBrand_id] IS NOT NULL
) AS [t17]
) AS [t18]
where [t1].[contact_id] = [t18].[contact_id]
) AS [t19])
该代码使用应用。如果 replace Apply by Left Join 代码会在几次内运行 faster。 如何强制 Linq 使用 Left Join 生成代码?
【问题讨论】:
LINQ to SQL 不是编写 SQL 的“更好”方式,也不是替代 SQL。手动编写这样一个简单的语句并让 L2S 从结果中创建新对象更加容易和快捷 这个解决方案使得自动重构不可用。 请向我们展示完整的 LINQ 查询... 另外请张贴图形执行计划。我看不出为什么 APPLY 应该在此处产生不同的查询计划的原因。你确定到LEFT JOIN
的转换在语义上是相同的吗?
计划与 Apply yadi.sk/d/w9SAcsiu1VDKU
【参考方案1】:
我找不到为什么 a) join 和 apply 产生不同的计划和 b) LINQ 完全使用 apply 的原因。这是一个奇怪的案例。
我尝试了解决方法,希望它们有效:
//project only the needed columns
var contactInfos = from person in persons
join tempDesiredCar in
desiredCars.Select(x => new x.groupEntity_id, x.name )
on person.contact_id equals tempDesiredCar.groupEntity_id
into tempDesiredCars
from desiredCar in tempDesiredCars.DefaultIfEmpty()
select new name = person.name, car = desiredCar.name ;
//change join to from
var contactInfos = from person in persons
from desiredCar in
desiredCars.Select(x => new x.groupEntity_id, x.name )
.Where(x => person.contact_id = x.groupEntity_id)
.DefaultIfEmpty()
select new name = person.name, car = desiredCar.name ;
这些只是推测性的重写。我希望他们能巧合地工作......我知道这不是解决问题的好方法,但在这种奇怪的情况下,这是我能提供的最好的方法。
【讨论】:
第二个 LINQ 表达式,LINQ 没有完全翻译成 SQL。【参考方案2】:我检查了查询计划。
应用计划使用嵌套循环将表连接在一起。对于 Person 表中的每条记录,它读取 DesiredCars 表中的每条记录。你真的需要在数据库中找到每个人想要的车吗?如果没有,请考虑先过滤一下 Person 表。
连接计划使用哈希匹配将表连接在一起。它读取两个表一次,生成一个表的映射,并在第二个表上使用该映射。优化器指出了一些索引建议。这些建议可能对这两个计划都有帮助。
<MissingIndexes>
<MissingIndexGroup Impact="12.6549">
<MissingIndex Database="[AudiCRM]" Schema="[dbo]" Table="[DesiredCar]">
<ColumnGroup Usage="INEQUALITY">
<Column Name="[carBrand_id]" ColumnId="13" />
</ColumnGroup>
<ColumnGroup Usage="INCLUDE">
<Column Name="[id]" ColumnId="1" />
<Column Name="[contact_id]" ColumnId="2" />
</ColumnGroup>
</MissingIndex>
</MissingIndexGroup>
<MissingIndexGroup Impact="12.6549">
<MissingIndex Database="[AudiCRM]" Schema="[dbo]" Table="[DesiredCar]">
<ColumnGroup Usage="INEQUALITY">
<Column Name="[model]" ColumnId="3" />
</ColumnGroup>
<ColumnGroup Usage="INCLUDE">
<Column Name="[id]" ColumnId="1" />
<Column Name="[contact_id]" ColumnId="2" />
</ColumnGroup>
</MissingIndex>
</MissingIndexGroup>
</MissingIndexes>
【讨论】:
【参考方案3】:我找到了 LINQ 使用应用运算符的原因。操作数正在检查该连接中是否存在与外键的依赖关系。如果它只是一个表 - linq 使用“左连接”。如果子查询包含 Group By、Order By 以及在“equals”中使用的列,则 LINQ 使用“Outer apply”。 在 LINQ JOIN 中使用简单的子查询。
【讨论】:
我已经阅读了您的答案 10 分钟,但我仍然不明白。我不明白你的句子,我不明白你所说的“简单子查询”是什么意思。由于不同的原因,我自己试图避免使用“APPLY”运算符。以上是关于LINQ 到 SQL。如何防止使用 Apply 运算符的主要内容,如果未能解决你的问题,请参考以下文章
与布尔值 false 进行比较时,如何防止 Linq2Sql 否定位列?
使用 LINQ to SQL 是不是有助于防止 SQL 注入