gpt4 book ai didi

sql - 左连接生成的额外子查询

转载 作者:行者123 更新时间:2023-12-04 20:10:32 25 4
gpt4 key购买 nike

我使用标准的 GroupJoin/SelectMany/DefaultIfEmpty 方法编写了一个在 LINQ 中执行左连接的方法:

public static IQueryable<TResult> LeftJoin<TLeft, TRight, TKey, TResult>(
this IQueryable<TLeft> left,
IEnumerable<TRight> right,
Expression<Func<TLeft, TKey>> leftKeySelector,
Expression<Func<TRight, TKey>> rightKeySelector,
Expression<Func<TLeft, TRight, TResult>> resultSelector)
{
var paramL = Expression.Parameter(typeof(TLeft), "l");
var paramR = Expression.Parameter(typeof(TRight), "r");
var paramRs = Expression.Parameter(typeof(IEnumerable<TRight>), "rs");

var expr = Expression.Lambda<Func<TLeft, IEnumerable<TRight>, IEnumerable<TResult>>>(
Expression.Call(
typeof(Enumerable),
"Select",
new [] { typeof(TRight), typeof(TResult) },
Expression.Call(typeof(Enumerable), "DefaultIfEmpty", new[] { typeof(TRight) }, paramRs),
Expression.Lambda<Func<TRight, TResult>>(
Expression.Invoke(resultSelector, paramL, paramR),
paramR)),
paramL,
paramRs
);

return left
.GroupJoin(
right,
leftKeySelector,
rightKeySelector,
expr)
.SelectMany(x => x);
}

我是这样测试的:

var q = myDB.PurchaseOrderHeaders
.LeftJoin(
myDB.PurchaseOrderLines,
po => po.PurchaseOrderGUID,
line => line.PurchaseOrderGUID,
(po, line) => new { PO = po, Line = line }
);

var e = q.AsEnumerable();

我期望的 SQL 是这样的:

SELECT [t0].[PurchaseOrderGUID], ..., [t1].[PurchaseOrderLineGUID], ...
FROM [dbo].[PurchaseOrderHeader] AS [t0]
LEFT OUTER JOIN [dbo].[PurchaseOrderLine] AS [t1]
ON [t0].[PurchaseOrderGUID] = [t1].[PurchaseOrderGUID]

但是得到了这个:

SELECT [t0].[PurchaseOrderGUID], ..., [t2].[test], [t2].[PurchaseOrderLineGUID], ...
FROM [dbo].[PurchaseOrderHeader] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[PurchaseOrderLineGUID], ...
FROM [dbo].[PurchaseOrderLine] AS [t1]
) AS [t2] ON [t0].[PurchaseOrderGUID] = [t2].[PurchaseOrderGUID]

区别在于带有SELECT 1 as [test] 的子查询。为什么会产生这个?它可能会对性能产生重大影响吗?如果是这样,我可以修改查询以消除它吗?

最佳答案

(免责声明:我对 LINQ 了解不多。以下是基于我对 SQL 的了解,以及对 LINQ 试图做什么的有根据的推断。)

Why is it generating this?

我推测 1 AS [test] 的目的是为 LINQ 提供一种清晰、简单、一致且明确的方式来区分“PurchaseOrderLine 中没有匹配的记录” “来自“PurchaseOrderLine 中的一条匹配记录”。您可能认为您可以通过检查 PurchaseOrderLineGUID 和其他字段来区分它们,这在您的情况下可能是正确的;但在一般情况下,如果 LEFT JOIN 成功加入记录,但从该记录中选择的所有字段均为空,会发生什么情况? (在你的情况下,这是不可能的,因为 PurchaseOrderLineGUID 是(我假设)不可为空的,但 LINQ 知道吗?尽管就此而言,即使不知道哪些表列是不可空的,人类查询编写者可以通过在顶级字段列表中使用 [t2].[PurchaseOrderGuid] AS [test] 来避免子查询,因为 ON 子句防止匹配成功时 [t2].[PurchaseOrderGuid] 为 null 的可能性;但我不确定这对 LINQ 来说有多明显。)

Is it likely to have any significant effect on performance?

不应该;因为 1 AS [test] 没有在任何可能真正影响查询语义的地方使用(例如,在 WHEREON GROUP BYHAVING 子句),SQL Server 应该能够执行“谓词下推”以(在某种意义上)将 ON 条件移动到子查询中,并在 PurchaseOrderHeaderPurchaseOrderLine 之间执行常规索引散列连接以确定它需要哪些记录。 1 AS [test] 只会在组装结果集时添加,用于实际选择的 PurchaseOrderLine 记录。

(我之所以这样说,部分是因为我知道 SQL Server 擅长谓词下推 — even in the rare cases where that turns out to be a bad thing — 部分是因为,如上所述,LINQ 可以避免在此创建子查询案例。我想 LINQ 团队知道他们在做什么,如果他们认为子查询可能会有性能损失,我猜 LINQ 会更加努力地确定给定案例是否真的需要子查询。因为 LINQ 不不打扰,估计是因为没关系吧。)

关于sql - 左连接生成的额外子查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12747476/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com